首先,* 不是通配符!它通常也发音为“star”。
Bleeding edge note:截至 2015 年 2 月有 a proposal to simplify GHC's subkind system (in 7.12 or later)。该页面包含对 GHC 7.8/7.10 故事的精彩讨论。展望未来,GHC 可能会放弃类型和种类之间的区别,* :: *。见Weirich, Hsu, and Eisenberg, System FC with Explicit Kind Equality。
标准:类型表达式的描述。
Haskell 98 报告defines * in this context as:
符号*代表所有空类型构造函数的种类。
在这种情况下,“nullary”只是意味着构造函数不带参数。 Either 是二进制的;它可以应用于两个参数:Either a b。 Maybe 是一元的;它可以应用于一个参数:Maybe a。 Int 为空;它可以应用于 no 参数。
这个定义本身有点不完整。包含完全应用的一元、二元等类型构造函数的表达式也有种类*,例如Maybe Int :: *.
在 GHC 中:包含值的东西?
如果我们翻阅 GHC 文档,我们会发现更接近“可以包含运行时值”的定义。 GHC Commentary page "Kinds" 声明“'*' 是一种装箱值。像Int 和Maybe Float 这样的东西有一种*。”另一方面,GHC user's guide for version 7.4.1 表示* 是一种“提升类型”。 (当该部分被修改时,该段落没有保留
PolyKinds.)
盒装值和提升类型有点不同。根据GHC Commentary page "TypeType",
一个类型被拆箱,当且仅当它的表示不是一个指针。未装箱的类型也未提升。
一个类型被提升当它有底部作为一个元素。闭包总是具有提升类型:即 Core 中的任何 let-bound 标识符都必须具有提升类型。在操作上,提升的物体是可以进入的。只有提升的类型可以与类型变量统一。
所以ByteArray#,原始内存块的类型,是装箱,因为它表示为一个指针,但unlifted,因为底部不是一个元素。
> undefined :: ByteArray#
Error: Kind incompatibility when matching types:
a0 :: *
ByteArray# :: #
因此,旧用户指南的定义似乎比 GHC 评论中的定义更准确:* 是 提升 类型。(相反, # 是那种 unlifted 类型。)
请注意,如果类型* 总是被提升,对于任何类型t :: *,您可以使用undefined :: t 或其他一些机制来构造一个“值”来创建底部。因此,即使像 Void 这样的“逻辑上无人居住”的类型也可以有一个值,即底部。
所以看起来,是的,* 代表可以包含运行时值的类型,如果 undefined 是您对运行时值的想法。 (我不认为这不是一个完全疯狂的想法。)
GHC 扩展?
有几个扩展使善良系统活跃了一点。其中一些是平凡的:KindSignatures 让我们编写种类注解,就像类型注解一样。
ConstraintKinds 添加了Constraint 类型,大致就是=> 左侧的类型。
DataKinds 让我们引入除了* 和# 之外的新类型,就像我们可以通过data、newtype 和type 引入新类型一样。
使用DataKinds,每个data 声明(可能适用条款和条件)都会生成一个提升的种类声明。所以
data Bool = True | False
介绍常用的值构造函数和类型名称;此外,它还产生一个新的种类,Bool,以及两种类型:True :: Bool 和False :: Bool。
PolyKinds 引入了种类变量。这只是一种说“对于任何类型k”的方式,就像我们在类型级别说“对于任何类型t”一样。至于我们的朋友* 以及它是否仍然意味着“具有值的类型”,我想你可以说一个类型t :: k 其中k 是一种类型的变量可以包含值,如果@987654376 @ 或k ~ #。