为什么我可以这样做:
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
这看起来很奇怪!它曾经绊倒过你吗?
注:我知道"1"one_answers"2.0"是多态常数。这不是我担心的。让我担心的是为什么haskell在第一种情况下做了一件的事情,而在第二种情况下又做了另一件 !
(+)
的类型签名被定义为Num a => a -> a -> a
,这意味着它适用于Num
类型类的任何成员,但两个参数必须是相同的类型。
这里的问题是GHCI和它建立类型的顺序,而不是Haskell本身。如果你把你的任何一个例子放在一个文件中(使用do
let
表达式),它将编译和运行良好,因为GHC将使用整个函数作为上下文来确定字面量1
和2.0
的类型。
在第一种情况下发生的是GHCI猜测你输入的数字的类型。最精确的是Double
,所以它只是假设另一个应该是Double
并执行计算。然而,当您使用let
表达式时,它只有一个数字可以计算,因此它决定1
是Integer
, 2.0
是Double
。
第一个有效,因为数字字面值是多态的(它们被解释为fromInteger literal
resp)。fromRational literal
),所以在1 + 2.0
中,您实际上有fromInteger 1 + fromRational 2
,在没有其他约束的情况下,结果类型默认为Double
。
name = expresion
)绑定某些东西,则该实体被分配为单态类型。对于字面量1
,我们有一个Num
约束,因此,根据默认规则,它的类型在绑定let a = 1
中默认为Integer
。类似地,分数字面值的类型默认为Double
。
顺便说一下,如果在ghci中设置:set -XNoMonomorphismRestriction
,它将工作。
单态限制的原因是为了防止共享的损失,如果你看到一个值看起来像一个常量,你不希望它被计算不止一次,但如果它是一个多态类型,它将被重新计算每次使用
您可以使用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
其他人已经很好地解决了这个问题的许多方面。我想说一下为什么+
具有Num a => a -> a -> a
类型签名的基本原理。
首先,Num
类型类无法将Num
的任意实例转换为另一个实例。假设我有一个虚数的数据类型;它们仍然是数字,但你真的不能正确地将它们转换成一个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的第一步是首先忘掉你认为你知道的关于编程的一切。我相信大多数(如果不是全部的话)有经验的Haskellers都被Num类型类和Haskell的其他各种好奇心绊倒了,因为大多数人都先学习了一种更"主流"的语言。但是要有耐心,你最终会到达Haskell的天堂。:)