为什么我不能在Haskell中将Integer添加到Double?

时间:2011-11-24 20:06:41

标签: haskell

为什么我能这样做:

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在第一种情况下会做一个的事情,而在第二种情况下却另一个

4 个答案:

答案 0 :(得分:20)

(+)的类型签名定义为Num a => a -> a -> a,这意味着它适用于Num类型类的任何成员,但两个参数必须属于同一类型。

这里的问题是GHCI及其建立类型的顺序,而不是Haskell本身。如果你要将你的任何一个例子放在一个文件中(使用do用于let表达式),它将编译并运行正常,因为GHC将使用整个函数作为上下文来确定类型文字12.0

第一种情况发生的一切都是GHCI正在猜测你输入的数字的类型。最精确的是Double,因此它假定另一个应该是Double并执行计算。但是,当您使用let表达式时,它只能使用一个数字,因此它决定1Integer2.0Double }。

编辑:GHCI并非真正“猜测”,它使用的是Haskell报告中定义的非常具体的类型默认规则。您可以阅读更多关于here的内容。

答案 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是一个常量,可以是任何数字类型(DoubleInteger等。)

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的其他各种好奇心绊倒,因为大多数人首先学会了更“主流”的语言。但要耐心等待,你最终会达到哈斯克尔的必杀技。 :)