我对这为何如此有效感到困惑:
f :: Num a => a -> a
f x = x * 42
但这并不是:
g :: Num a => a -> a
g x = x * 4.2
我理解Num
包含了实施运算符(+)
,(-)
,(*)
的所有类型。因此,如果42
是Int
且4.2
是Fractional
,并且这两种类型都实现了运算符(*)
,为什么我会为{{{{}}收到此错误1}}:
g
答案 0 :(得分:5)
问题是Num a
约束过于宽泛。使用解释器可能有所帮助:
Prelude> :type 42
42 :: Num t => t
Prelude> :type 4.2
4.2 :: Fractional t => t
Haskell中的数字文字是多态的,因此正如类型所说,只要类型是Num
或Fractional
,文字就会被实例化为任何类型。
现在查看(*)
类中Num
函数的类型签名:
Prelude> :type (*)
(*) :: Num a => a -> a -> a
(*)
运算符适用于任何Num
值,但这两个操作数的属于同一类型。因此,如果您乘以Fractional类型的值(文本4.2
将被实例化的任何类型),另一个操作数也必须是Fractional,特别是相同的类型。这就是为什么函数的Num
约束太宽泛了:你的函数不能在任何Num
类型上工作,而只能在Fractional
上工作,因此编译器会说你错过了类型签名中的Fractional a =>
约束。
如果让编译器推断出函数的类型,它会同意:
Prelude> let g x = x * 4.2
Prelude> :type g
g :: Fractional a => a -> a
答案 1 :(得分:4)
Haskell类型的工作方式与您期望的不同。
Num a => a -> a
并不意味着"某些类型a
是Num
的实例,其值为a
并返回类型a
的值。"
这意味着"对于作为a
实例的每个类型Num
,它采用类型a
的值并返回类型{{1}的值}"
这意味着它必须适用于调用者选择的任何类型。如果调用者选择a
(a ~ Int
是类型相等的语法,某些可选GHC功能启用了作为约束)然后调用~
,那么g 1
值应该是Int
是
嗯,这样做不是很好。 4.2
不是有效的Int
。因此*
运算符无法进行类型检查,因为它无法使其参数类型匹配。
这就是为什么它具有更严格的类型 - 它接受的类型必须是文字4.2
是有效表示的类型。最终需要Fractional
约束。
Haskell中的类型变量是普遍量化的。他们总是声明满足其约束的所有类型。如果有可能的反例,事情就不会进行类型检查。
答案 2 :(得分:2)
数字常量42
定义为与fromIntegral (42::Integer)
相同,Num
的任何实例都具有fromIntegral
的实现。它出现在表达式x * 42
中,因此其推断类型与x
相同,instance
为Num
。因此,编译器为fromIntegral
Num
调用instance
的正确版本(或者更确切地说,在编译时进行计算),并且所有内容都是hunky-dorey。
但是带小数点的常量会尝试从小数转换为x
的类型。我们对该类型的所有了解都是instance
Num
,因此在一般情况下无法进行转换。没有办法将4.2
表示为Integer
。
正如错误消息所示,编译器可以将十进制常量转换为任何Fractional
类型。因此,将类型约束从Num
更改为Fractional
,小数将起作用。