Haskell:对Num类型类的使用感到困惑

时间:2018-02-07 02:19:09

标签: haskell typeclass

我对这为何如此有效感到困惑:

f :: Num a => a -> a
f x = x * 42

但这并不是:

g :: Num a => a -> a
g x = x * 4.2

我理解Num包含了实施运算符(+)(-)(*)的所有类型。因此,如果42Int4.2Fractional,并且这两种类型都实现了运算符(*),为什么我会为{{{{}}收到此错误1}}:

g

3 个答案:

答案 0 :(得分:5)

问题是Num a约束过于宽泛。使用解释器可能有所帮助:

Prelude> :type 42
42 :: Num t => t
Prelude> :type 4.2
4.2 :: Fractional t => t

Haskell中的数字文字是多态的,因此正如类型所说,只要类型是NumFractional,文字就会被实例化为任何类型。

现在查看(*)类中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并不意味着"某些类型aNum的实例,其值为a并返回类型a的值。"

这意味着"对于作为a实例的每个类型Num,它采用类型a的值并返回类型{{1}的值}"

这意味着它必须适用于调用者选择的任何类型。如果调用者选择aa ~ Int是类型相等的语法,某些可选GHC功能启用了作为约束)然后调用~,那么g 1值应该是Int

嗯,这样做不是很好。 4.2不是有效的Int。因此*运算符无法进行类型检查,因为它无法使其参数类型匹配。

这就是为什么它具有更严格的类型 - 它接受的类型必须是文字4.2是有效表示的类型。最终需要Fractional约束。

Haskell中的类型变量是普遍量化的。他们总是声明满足其约束的所有类型。如果有可能的反例,事情就不会进行类型检查。

答案 2 :(得分:2)

数字常量42定义为与fromIntegral (42::Integer)相同,Num的任何实例都具有fromIntegral的实现。它出现在表达式x * 42中,因此其推断类型与x相同,instanceNum。因此,编译器为fromIntegral Num调用instance的正确版本(或者更确切地说,在编译时进行计算),并且所有内容都是hunky-dorey。

但是带小数点的常量会尝试从小数转换为x的类型。我们对该类型的所有了解都是instance Num,因此在一般情况下无法进行转换。没有办法将4.2表示为Integer

正如错误消息所示,编译器可以将十进制常量转换为任何Fractional类型。因此,将类型约束从Num更改为Fractional,小数将起作用。