为什么我能这样做:
1 + 2.0
但是当我尝试时:
let a = 1
let b = 2.0
a + b
<interactive>:1:5:
Couldn't match expected type `Integer' with actual type `Double'
In the second argument of `(+)', namely `b'
In the expression: a + b
In an equation for `it': it = a + b
这看起来很奇怪!它会绊倒你吗?
P.S。:我知道“1”和“2.0”是多态常数。这不是让我担心的问题。让我担心的是为什么haskell在第一种情况下会做一个的事情,而在第二种情况下却另一个!
答案 0 :(得分:20)
(+)
的类型签名定义为Num a => a -> a -> a
,这意味着它适用于Num
类型类的任何成员,但两个参数必须属于同一类型。
这里的问题是GHCI及其建立类型的顺序,而不是Haskell本身。如果你要将你的任何一个例子放在一个文件中(使用do
用于let
表达式),它将编译并运行正常,因为GHC将使用整个函数作为上下文来确定类型文字1
和2.0
。
第一种情况发生的一切都是GHCI正在猜测你输入的数字的类型。最精确的是Double
,因此它假定另一个应该是Double
并执行计算。但是,当您使用let
表达式时,它只能使用一个数字,因此它决定1
是Integer
而2.0
是Double
}。
答案 1 :(得分:11)
第一个有效,因为数字文字是多态的(它们被解释为fromInteger literal
resp。fromRational literal
),所以在1 + 2.0
,你真的有{{ 1}},在没有其他约束的情况下,结果类型默认为fromInteger 1 + fromRational 2
。
由于单态限制,第二个不工作。如果绑定没有类型签名和简单模式绑定(Double
)的内容,则会为该实体分配一个单形类型。对于文字name = expresion
,我们有1
约束,因此,根据defaulting rules,其类型默认为绑定Num
中的Integer
。同样,小数文字的类型默认为let a = 1
。
顺便说一下,如果你在ghci中Double
,那么它会起作用。
单态限制的原因是为了防止共享丢失,如果你看到一个看起来像常量的值,你不会指望它被计算多次,但如果它有多态类型,它会每次使用时都要重新计算。
答案 2 :(得分:8)
您可以使用GHCI了解更多相关信息。使用命令:t
获取表达式的类型。
Prelude> :t 1
1 :: Num a => a
所以1
是一个常量,可以是任何数字类型(Double
,Integer
等。)
Prelude> let a = 1
Prelude> :t a
a :: Integer
所以在这种情况下,Haskell推断出a
的具体类型是Integer
。同样,如果您编写let b = 2.0
,则Haskell会推断类型Double
。使用let
使Haskell推断出比(或许)更具体的类型是必要的,这会导致您的问题。 (比我有更多经验的人也许可以评论为什么会出现这种情况。)由于(+)
的类型为Num a => a -> a -> a
,因此两个参数需要具有相同的类型。
您可以使用fromIntegral
函数修复此问题:
Prelude> :t fromIntegral
fromIntegral :: (Num b, Integral a) => a -> b
此函数将整数类型转换为其他数字类型。例如:
Prelude> let a = 1
Prelude> let b = 2.0
Prelude> (fromIntegral a) + b
3.0
答案 3 :(得分:3)
其他人已经很好地解决了这个问题的许多方面。我想谈谈为什么+
具有类型签名Num a => a -> a -> a
背后的理由。
首先,Num
类型类无法将Num
的一个artbitrary实例转换为另一个。{1}}。假设我有一个虚数的数据类型;它们仍然是数字,但你真的无法正确地将它们转换为Int
。
其次,您更喜欢哪种类型的签名?
(+) :: (Num a, Num b) => a -> b -> a
(+) :: (Num a, Num b) => a -> b -> b
(+) :: (Num a, Num b, Num c) => a -> b -> c
在考虑其他选项后,您意识到a -> a -> a
是最简单的选择。多态结果(如上面的第三个建议)很酷,但有时候太通用了,不能方便使用。
第三,Haskell不是Blub。大多数(尽管可能不是全部)关于Haskell的设计决策没有考虑到流行语言的惯例和期望。我经常喜欢说学习Haskell的第一步是忘掉你认为首先编程的一切。我敢肯定,大多数(如果不是全部)经验丰富的Haskeller都会被Num类型类以及Haskell的其他各种好奇心绊倒,因为大多数人首先学会了更“主流”的语言。但要耐心等待,你最终会达到哈斯克尔的必杀技。 :)