我有这个示例代码:
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
Run Code Online (Sandbox Code Playgroud)
此代码无法使用此错误进行编译:
test.hs:25:34:错误:
Run Code Online (Sandbox Code Playgroud)• 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为什么它说,它无法比拟Int用c?!
上面的代码只是我为大学项目构建的java编译器的一小部分,getnumberfromvalue只是一个实用函数,JavaValue是代表一个java文字.
我知道fromIntegral和realToFrac我不认为我可以在这里使用它们.
我已经看到了这个问题,但答案使用GADT和ExistentialQuantification似乎只适用于类型,我不认为我想用它来解决这个简单的问题,即使没有GADT也没有一种方法不涉及定义一个琐碎的数据类型?.
你想要表达的是存在主义类型:
{-# 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
...
Run Code Online (Sandbox Code Playgroud)
这基本上等同于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
...
Run Code Online (Sandbox Code Playgroud)
标准的多态Haskell签名之类的Real c => JavaValue -> Maybe c意思是非常不同,并且比存在类型更有用:它意味着调用者可以选择要使用的数字类型.这可以是在许多功能中共享的固定类型,在保留动态语言的灵活性的同时,不需要在两者之间进行转换.它与C++模板更相似,而不是通用的协变OO多态.
但是当然这意味着你必须在开始任何计算之前解决一个特定的类型,你不能只传递未知类型的数字(这将导致大量的运行时开销,这基本上是Ruby和Python很慢,Java有非对象类型,如int).
† IMO这是愚蠢的,因为真正的数字是实实在在的,一般不理性的.Real a应该可以说是toCauchySeq :: a -> [(Rational, Rational)]相反的方法.(我估计你不会喜欢那个,不过......)