我有这个示例代码:
module Main where
import Data.Int
import Data.Text
data JavaValue = JavaString Text
| JavaByte Int8
| JavaShort Int16
| JavaInt Int32
| JavaLong Int64
| JavaFloat Float
| JavaDouble Double
| JavaChar Char
| JavaBool Bool
| ArrayValue [JavaValue]
| JavaNull deriving (Eq)
getnumberfromvalue :: Real c => JavaValue -> Maybe c
getnumberfromvalue (JavaByte n) =Just n
getnumberfromvalue (JavaShort n) =Just n
getnumberfromvalue (JavaInt n) =Just n
getnumberfromvalue (JavaLong n) =Just n
getnumberfromvalue (JavaFloat n) =Just n
getnumberfromvalue (JavaDouble n) =Just n
getnumberfromvalue (JavaChar n) =Just $ fromEnum n
getnumberfromvalue _ = Nothing
此代码无法使用此错误进行编译:
test.hs:25:34:错误:
• Couldn't match type ‘c’ with ‘Int’ ‘c’ is a rigid type variable bound by the type signature for: getnumberfromvalue :: forall c. Real c => JavaValue -> Maybe c at test.hs:18:23 Expected type: Maybe c Actual type: Maybe Int • In the expression: Just $ fromEnum n In an equation for ‘getnumberfromvalue’: getnumberfromvalue (JavaChar n) = Just $ fromEnum n • Relevant bindings include getnumberfromvalue :: JavaValue -> Maybe c (bound at test.hs:19:1)
我不明白为什么,因为Int(8,16,32,64)
和Float
以及Double
都是Real
,为什么它说它不匹配{{} 1}}与Int
?!!
上面的代码只是我为大学项目构建的java编译器的一小部分,c
只是一个实用函数,getnumberfromvalue
代表一个java文字。
我知道JavaValue
和fromIntegral
,我不认为我可以在这里使用它们。
我看过this问题,但答案是使用GADT,ExistentialQuantification似乎只适用于类型,我不认为我想用它来解决这个简单的问题,即使没有GADT也没有#n;'有没有一种方法不涉及定义一个简单的数据类型?
答案 0 :(得分:9)
您要表达的是存在类型:
{-# LANGUAGE GADTs #-}
data SomeReal where
SomeReal :: Real c => c -> SomeReal
getnumberfromvalue :: JavaValue -> Maybe SomeReal
getnumberfromvalue (JavaByte n) = Just $ SomeReal n
getnumberfromvalue (JavaShort n) = Just $ SomeReal n
getnumberfromvalue (JavaInt n) = Just $ SomeReal n
...
这基本上等同于OO语言对Real getNumberFromValue (JavaValue v) {...}
这样的签名的意义:函数返回一些某种类型,调用者不知道它是什么类型的将(只是它在Real
类中)。
但你为什么要这样呢?您可以使用多种未知类型做多少,您甚至无法添加它或与其他数字进行比较,因为类型可能不同。由于Real
具体而言, 所做的唯一事情是将数字转换为具有类型Rational
的数字(被认为是“所有真正的†数字类型的超类型”)。好的,但是你可以在前面这样做,不需要在存在主义中包装:
getnumberfromvalue :: JavaValue -> Maybe Rational
getnumberfromvalue (JavaByte n) = Just $ toRational n
getnumberfromvalue (JavaShort n) = Just $ toRational n
getnumberfromvalue (JavaInt n) = Just $ toRational n
...
像Real c => JavaValue -> Maybe c
这样的标准多态Haskell签名的含义是非常不同的,并且比存在类型更有用:它意味着调用者可以选择要使用的数字类型。这可以是在许多功能中共享的固定类型,在保留动态语言的灵活性的同时,不需要在两者之间进行转换。它与C ++模板更相似,而不是通用的协变OO多态性
但是当然这意味着你必须在开始任何计算之前解决某个特定的类型,你不能只传递未知类型的数字(这会产生大量的运行时开销,基本上是Ruby和Python速度慢的原因,而Java有非int
的非对象类型。
† IMO这很愚蠢,因为real numbers通常不是理性。 Real a
的方法可以说是toCauchySeq :: a -> [(Rational, Rational)]
。 (我估计你不会那么喜欢,但是......)
答案 1 :(得分:2)
你可以这样做:
getnumberfromvalue :: Fractional f => JavaValue -> Maybe f
getnumberfromvalue (JavaByte n) = Just . realToFrac $ n
getnumberfromvalue (JavaShort n) = Just . realToFrac $ n
getnumberfromvalue (JavaInt n) = Just . realToFrac $ n
getnumberfromvalue (JavaLong n) = Just . realToFrac $ n
getnumberfromvalue (JavaFloat n) = Just . realToFrac $ n
getnumberfromvalue (JavaDouble n) = Just . realToFrac $ n
getnumberfromvalue (JavaChar n) = Just . realToFrac . fromEnum $ n
getnumberfromvalue _ = Nothing
或者像这样:
getnumberfromvalue :: (Num a, Typeable a) => JavaValue -> Maybe a
getnumberfromvalue (JavaByte n) = Just . fromIntegral $ n
getnumberfromvalue (JavaShort n) = Just . fromIntegral $ n
getnumberfromvalue (JavaInt n) = Just . fromIntegral $ n
getnumberfromvalue (JavaLong n) = Just . fromIntegral $ n
getnumberfromvalue (JavaFloat n) = cast n <|> cast (realToFrac n :: Double)
getnumberfromvalue (JavaDouble n) = cast n <|> cast (realToFrac n :: Float)
getnumberfromvalue (JavaChar n) = Just . fromIntegral . fromEnum $ n
getnumberfromvalue _ = Nothing