我正在学习 Haskell 并遇到一个未解答的问题:
从我的教训来看:
(+) :: Num a => a -> a -> a
Run Code Online (Sandbox Code Playgroud)
对于任何数字类型
a, (+) 接受 2 个类型的值a并返回一个类型的值a。
根据示例:
1 + 1
-- 2 # type a is Int
3.0 + 4.0
-- 7.0 type a is Float
'a' + 'b'
-- Type Error: Char is not a Numeric type
Run Code Online (Sandbox Code Playgroud)
这是完全有道理的,但我最终不明白后台发生了什么,当:
1 + 3.0
Run Code Online (Sandbox Code Playgroud)
类型推断系统是否会自动将我的 Int 转换为 Float,因为它知道它将返回 Float?
您应该在 ghci 中调查此类问题。这是非常宝贵的学习资源:
\n$ ghci\nGHCi, version 9.0.1: https://www.haskell.org/ghc/ :? for help\nghci> :t 1\n1 :: Num p => p\nghci> :t 3.0\n3.0 :: Fractional p => p\nghci> :t 1 + 3.0\n1 + 3.0 :: Fractional a => a\nRun Code Online (Sandbox Code Playgroud)\n第一课:数字文字是多态的。1不是 an Int,它是多态的。它可以是Num代码编译所需的任何实例。3.0不是 a Float,它是Fractional代码编译所必需的任何实例。(区别在于文字中的小数 - 它限制了允许的类型。)
第二课:当你将事物组合成一个表达式时,类型就会统一。当你统一Num和Fractional约束时,你就会得到Fractional约束。这是因为Fractional被定义为要求它的所有实例也是 的实例Num。
更多信息,让我们打开警告并查看它们提供了哪些附加信息。
\nghci> :set -Wall\nghci> 1\n\n<interactive>:5:1: warning: [-Wtype-defaults]\n \xe2\x80\xa2 Defaulting the following constraints to type \xe2\x80\x98Integer\xe2\x80\x99\n (Show a0) arising from a use of \xe2\x80\x98print\xe2\x80\x99 at <interactive>:5:1\n (Num a0) arising from a use of \xe2\x80\x98it\xe2\x80\x99 at <interactive>:5:1\n \xe2\x80\xa2 In a stmt of an interactive GHCi command: print it\n1\nghci> 1 + 3.0\n\n<interactive>:6:1: warning: [-Wtype-defaults]\n \xe2\x80\xa2 Defaulting the following constraints to type \xe2\x80\x98Double\xe2\x80\x99\n (Show a0) arising from a use of \xe2\x80\x98print\xe2\x80\x99 at <interactive>:6:1-7\n (Fractional a0) arising from a use of \xe2\x80\x98it\xe2\x80\x99 at <interactive>:6:1-7\n \xe2\x80\xa2 In a stmt of an interactive GHCi command: print it\n4.0\nRun Code Online (Sandbox Code Playgroud)\n打印值时,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是最常见的数值转换函数。)但是当值是多态时,这意味着推理可以选择匹配类型而无需转换。