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

And*_*yuk 16 haskell

为什么我能这样做:

1 + 2.0
Run Code Online (Sandbox Code Playgroud)

但是当我尝试:

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
Run Code Online (Sandbox Code Playgroud)

这看起来很奇怪!它会绊倒你吗?

PS:我知道"1"和"2.0"是多态常数.这不是让我担心的问题.我担心的是,为什么不哈斯克尔一个在第一种情况下的事情,但另一个在第二!

Jef*_*rka 20

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

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

在第一种情况下发生的所有事情都是GHCI猜测你输入的数字的类型.最精确的是a Double,所以它只是假设另一个应该是a Double并执行计算.但是,当你使用let表达式时,它只有一个数字可以解决,所以它决定1是一个Integer并且2.0是一个Double.

编辑:GHCI并不是真正"猜测",它使用的是Haskell报告中定义的非常具体的类型默认规则.你可以在这里阅读更多相关内容.

  • 首先,我投了赞成票.但由于我习惯于挑剔:静态类型系统可以支持添加不同类型的数量.只是Haskell定义的定义(`(+):: Num a => a - > a - > a`)不允许它.(虽然它的类型推断不太好,这可能是它没有完成的原因.)另外,如果你明确表示你的答案可能更清楚("猜测"不公正恕我直言)解释类型默认文字是多态的这一事实. (3认同)
  • "Haskell有一个静态类型系统,因此你永远无法将两种不同的类型组合在一起." 这有点过于简单了.许多语言都有静态类型系统,允许添加双精度.这里的关键字是Haskell没有隐式转换. (3认同)

Dan*_*her 11

第一个是有效的,因为数字文字是多态的(它们被解释为fromInteger literalresp.fromRational literal),所以在没有其他约束的情况下1 + 2.0,你确实有fromInteger 1 + fromRational 2结果类型默认为Double.

第二次却不会因为单态限制的工作.如果绑定没有类型签名和简单模式绑定(name = expresion)的东西,则该实体被赋予一个单形类型.对于文字1,我们有一个Num约束,因此,根据默认规则,其类型默认为Integer绑定let a = 1.同样,小数文字的类型默认为Double.

顺便说一下,如果你:set -XNoMonomorphismRestriction在ghci ,它会起作用.

单态限制的原因是为了防止共享丢失,如果你看到一个看起来像常量的值,你不希望它被计算多次,但是如果它有多态类型,它会在每次重新计算时重新计算它被使用了.


Cal*_*vin 8

您可以使用GHCI进一步了解这一点.使用该命令:t获取表达式的类型.

Prelude> :t 1
1 :: Num a => a
Run Code Online (Sandbox Code Playgroud)

所以1是一个常数,其可以是任何数值类型(Double,Integer等)

Prelude> let a = 1
Prelude> :t a
a :: Integer
Run Code Online (Sandbox Code Playgroud)

所以在这种情况下,Haskell推断出具体类型为ais 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
Run Code Online (Sandbox Code Playgroud)

此函数将整数类型转换为其他数字类型.例如:

Prelude> let a = 1
Prelude> let b = 2.0
Prelude> (fromIntegral a) + b
3.0
Run Code Online (Sandbox Code Playgroud)

  • [单态限制](http://www.haskell.org/haskellwiki/Monomorphism_restriction)是"let"导致选择特定类型的原因.您可以通过为其提供类型签名或在GHCi中执行`:set -XNoMonomorphismRestriction`(或编译时的等效标志)来覆盖它. (3认同)