我正在尝试理解Haskell的类型系统。我发现了以下内容:
*Main> :t ("Hello", 4)
("Hello", 4) :: Num t => ([Char], t)
*Main> :t ("Hello", 'a')
("Hello", 'a') :: ([Char], Char)
*Main> :t ("Hello", True)
("Hello", True) :: ([Char], Bool)
为什么("Hello", 4)
的类型没有像其他类型那样表示。我原以为它是("Hello", 4) :: ([Char], Num)
之前我已经看过=>
了。我想知道的是,为什么它会产生这种差异?
答案 0 :(得分:11)
那是因为Num
不是一个类型;它是typeclass,而Num t => someType
表示t
是某种任意类型,它是Num
类型类的实例。借用一些Java / C#术语,您可以将Num
视为接口,Num t => t
是一种通用类型,其约束条件t
必须实现Num
接口。
通常,您会在=>
箭头的左侧找到类型类约束,在右侧找到类型正文。我们可以有多个类约束,例如(Num a, Num b) => (a, b)
,它表示两个任意数字类型的元组的类型。我们也可以有零类约束,在这种情况下省略=>
。
在Haskell中,数字文字可以表示作为Num
实例的任何类型。文字4
可以表示浮点数或整数,或者(如果你定义一些更奇特的实例)甚至是函数。
答案 1 :(得分:2)
Num
是一个类型类。类型类似于(但不是真的)类似于OO接口。不同的类型实现了类型类的功能,允许多态性。
例如,有一个标准Eq
类,定义为(您可以使用:info Eq
在ghci中获取此内容)
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
这意味着您可以编写isMember :: (Eq a) => a -> [a] -> Bool
之类的函数。此函数采用类型a
实现(具有实例)Eq
和相同类型的列表,并返回布尔值。此通用类型签名意味着您可以执行
elem 'c' "abcd"
elem 4 [1,2,3]
它会编译,但你不能做
elem 'c' [1,2,3]
现在,回到原来的问题。
当您在ghci中键入数字时,它会尝试将该数字设为最常用的类型。它不知道5
是Int
,还是Integer
,甚至是Double
。因此,文字具有类型(Num t) => t
,这意味着“此事件可以具有任何类型t
,而不是实现Num
类型类别。”
不同的表达式会为您提供更具体的类型。例如,5 `div` 3
的类型为(Integral a) => a
,因为div
是Integral
类的方法。
如果您想了解有关类型类的更多信息,this是一个很好的链接。
答案 2 :(得分:2)
数字文字是一种特殊情况,4
可以是例如解释为Int
或Integer
,编译器不会为您做出选择,而是以公共类型类{{1}的形式采用一种“最小公分母” (正如其他答案已经解释过的那样)。
我试图提出的另一点是,如果你在这一点上更具体,那么古怪就会消失:
Num