您应该在 ghci 中调查这类问题。这是一个非常宝贵的学习资源:
$ ghci
GHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help
ghci> :t 1
1 :: Num p => p
ghci> :t 3.0
3.0 :: Fractional p => p
ghci> :t 1 + 3.0
1 + 3.0 :: Fractional a => a
第一课:数字文字是多态的。 1 不是 Int,它是多态的。它可以是编译代码所必需的Num 的任何实例。 3.0 不是Float,它是编译代码所必需的Fractional 的任何实例。 (区别在于文字中的小数 - 它限制了允许的类型。)
第二课:当你将事物组合成一个表达式时,类型就会统一起来。当你统一 Num 和 Fractional 约束时,你会得到一个 Fractional 约束。这是因为Fractional 被定义为要求它的所有实例也是Num 的实例。
更多信息,让我们打开警告,看看它们提供了哪些额外信息。
ghci> :set -Wall
ghci> 1
<interactive>:5:1: warning: [-Wtype-defaults]
• Defaulting the following constraints to type ‘Integer’
(Show a0) arising from a use of ‘print’ at <interactive>:5:1
(Num a0) arising from a use of ‘it’ at <interactive>:5:1
• In a stmt of an interactive GHCi command: print it
1
ghci> 1 + 3.0
<interactive>:6:1: warning: [-Wtype-defaults]
• Defaulting the following constraints to type ‘Double’
(Show a0) arising from a use of ‘print’ at <interactive>:6:1-7
(Fractional a0) arising from a use of ‘it’ at <interactive>:6:1-7
• In a stmt of an interactive GHCi command: print it
4.0
当打印一个值时,ghci 要求该类型有一个Show 实例。幸运的是,这里的细节并不太重要,但这就是为什么默认警告指的是Show。
这里要观察的教训是,如果推理不需要更具体的内容,则带有 Num 实例的默认类型是 Integer,而不是 Int。如果推理不需要更具体的东西,则带有Fractional 实例的东西的默认类型是Double,而不是Float。 (Float 基本没用过,算了吧。)
因此,当推理运行时,表达式1 + 3.0 被推断为具有Fractional a => a 类型。在对类型没有进一步要求的情况下,默认启动并显示“a 是Double”。然后该信息通过(+) 流回它的参数,并要求它们中的每一个也是Double。幸运的是,每个参数都是一个多态文字,可以采用Double 类型。类型检查成功,选择实例,添加,打印结果。
数字文字是多态的,这对这个过程非常重要。 Haskell 没有任何类型对之间的隐式转换。尤其不是数字类型。如果您想真正将值从一种类型转换 到另一种类型,则必须调用一个函数来进行所需的转换。 (fromIntegral、round、floor、ceiling 和 realToFrac 是最常见的数值转换函数。)但是当值是多态的时,这意味着推理可以选择匹配类型而无需转换。