* -> * 是一种类型签名的示例。种类可以被认为是“类型的类型”。也就是说,就像值有类型一样,类型也有种类。
种类
如果我们忽略一些语言扩展,种类仅由两个构造函数构建:
-
* 表示正确的类型,即那些被值所占据的类型,例如Int、Char、Maybe Int、[Char]。
-
k1 -> k2 形式的种类(其中k1 和k2 本身就是种类)指定类型构造函数, 即需要用一个或多个类型参数完成的类型表达式形成适当的类型。例如:类型构造函数Maybe 需要一个类型为* 的参数才能生成正确的类型,因此它有类型* -> *;同样适用于列表的类型构造函数[],因此也有种类* -> *;类型构造函数Either 采用两个类型为* 的参数来形成正确的类型(例如Either Int Char 或Either (Maybe Int) [Char]),因此它具有类型* -> * -> *。
高阶类型的种类
请注意,类型构造函数-> 关联到右侧。即* -> * -> * 与* -> (* -> *) 相同。对于所谓的高阶类型(即作为类型构造函数而不是正确类型作为其参数的类型)的示例,首先考虑玫瑰树的类型Rose:
data Rose a = Branch a [Rose a]
这是* -> * 的一阶类型。但是,如果我们通过抽象它对列表的类型构造函数的使用进行概括,我们会得到
data GRose f a = Branch a (f a)
它有一种(* -> *) -> * -> *,因此是二阶类型。
二阶类型的另一个例子是类型级定点运算符Fix:
newtype Fix f = In {out :: f (Fix f)}
实际上几乎不会出现高于两个的订单类型。我见过的为数不多的例子之一是Fix“提升为善良* -> *:
data HFix h a = HIn {hOut :: h (HFix h) a}
确实,HFix 和 ((* -> *) -> * -> *) -> * -> * 一样。
GHCi
GHC 的交互环境 (ghci) 提供命令 :kind(通常缩写为 :k)用于查询类型表达式的种类。例如:
> :k Either
Either :: * -> * -> *
> :k Either Int
Either Int :: * -> *
> :k Either Int Char
Either Int Char :: *
种类错误
只是一个程序可以包含类型错误,有可能引入类型错误。例如:
> :k Either Int Maybe
<interactive>:1:12:
Expecting one more argument to `Maybe'
In a type in a GHCi command: Either Int Maybe
这里我们为Either 提供了一个类型为* -> * 而不是* 的类型表达式作为第二个参数。
如果你将非函数类型的类型表达式(即*)应用于另一个类型表达式,这也是一个类型错误:
> :k Int Char
<interactive>:1:1:
`Int' is applied to too many type arguments
In a type in a GHCi command: Int Char