Haskell 具有三个命名空间,每个命名空间用于运行时计算、类型和模块。
任何表示运行时计算的术语都可以在计算命名空间中命名。 data 和 newtype 声明在计算命名空间中创建新名称,用于构造其新类型的值,如果使用记录语法,则用于从新类型中选择字段。 class 声明在计算命名空间中为其方法创建新名称。
任何单态类型都可以在类型命名空间中使用type 声明来命名(请参阅下面的 cmets 以了解我对本声明中令人困惑的细微之处的预测)。 data 和 newtype 声明在类型命名空间中创建新名称,用于构造它们声明的类型。 class 声明在类型命名空间中为它们创建的约束创建新名称。
module 声明在模块命名空间中创建新名称。
GHC 扩展了 Haskell,添加了多种新的方法来绑定名称(几乎都在类型命名空间中);对于这种格式,一个完整的列表可能太大了,但the manual 非常棒,涵盖了所有内容。
现在,到 type 的微妙之处。我预测会出现的一个混淆是:我说只能命名单态类型。但是有人可能会反对我当然可以写,例如
type TypeOfIdMono a = a -> a
id :: TypeOfIdMono a
看起来它已经命名了一个多态类型。我声称 Haskell 倾向于使 foralls 隐含反而混淆了这个问题,而 TypeOfId a 实际上是单态的。有了明确的 foralls,这已经写成:
type TypeOfIdMono a = a -> a
id :: forall a. TypeOfIdMono a
也就是说:我们实际上并没有在这里命名id的类型,而是只在as上运行的单态函数的类型。 id 还说调用者可以选择a - 也就是说,该函数是多态的。比较标准 Haskell 中不允许的声明(尽管可以通过 GHC 扩展获得,如上所述):
type TypeOfIdPoly = forall a. a -> a
id :: TypeOfIdPoly
这里我们确实命名了一个多态类型。
简而言之:可以而且应该区分三个正交概念:“参数化类型”(例如 TypeOfIdMono 需要一个附加参数)、提及类型变量的类型(例如 TypeOfIdMono a)和多态类型(例如TypeOfIdPoly) 必须有一个forall。