与其他类型级别的函数相比,Haskell中“类型构造函数”概念的独特之处是什么?
据我所知:
答案 0 :(得分:12)
短语“类型构造器”有两种常见用法。有些人将它们用于带有箭头的任何类型。但是Haskell报告以不同的方式一致地使用了它,因此我将讨论该定义。
构造函数由data
和newtype
声明创建,并且是这些声明在类型级别引入的单个新名称。以下是一些类型构造函数的示例:
Either
Maybe
[]
Bool
抱歉,您注意到最后一个吗?没错,“类型构造函数”一词并没有暗示它必须能够接受参数。 Bool
是由数据声明引入的类型级别名称-因此是类型构造函数。以下是一些不是构造函数的类型的示例:
Maybe Int
a -> b
Either ()
m -- even if we know, say, Monad m holds
糟糕,您注意到最后两个吗?是的,朝着另一个方向发展,没有什么能够采用进一步的类型参数使您成为类型构造函数的了。 Either
和()
都是构造函数,但是Either
到()
的应用不是构造函数,因为它不是{{1 }}或data
声明。同样,newtype
是类型变量,而不是构造函数-它的含义不受任何m
或data
声明的约束。
除构造函数和变量外,标准Haskell中还有另一种类型级别名称:类型别名。类型别名和构造函数之间有两个主要区别:
构造函数是内射的,别名可能不是。如果newtype
和FooC a b c
是同一类型,并且FooC a' b' c'
是构造函数,则FooC
和a
是同一类型,a'
和b
是同一类型,b'
和c
是同一类型。对比
c'
其中type FooA a = String
和FooA ()
是同一类型,即使FooA Bool
和()
不是同一类型。
构造函数可能被部分应用,类型别名不能被应用。例如,如果您写
Bool
然后type BarA a = Maybe a
无效-即使StateT Int BarA ()
仍然必须立即为BarA
赋予其类型参数。当然,
StateT Int Maybe ()
然后type BarEtaA = Maybe
再次有效,因为别名StateT Int BarEtaA ()
在扩展为定义的值BarEtaA
之前不需要任何参数。
别名和构造函数之间还有一些其他细微的差别,但它们并不是基本的(并且通过适当的GHC扩展而得到了缓解)。
我可以在标准Haskell中想到的构造函数和变量之间只有一个区别,即它们与类型类机制的交互。具体来说,实例的格式必须为Maybe
,约束/上下文的格式必须为instance <class> (<constructor> <variable> <variable> <variable> ...) where ...
。通过适当的GHC扩展,可以放宽这些限制。
GHC实施的扩展Haskell还包括其他两种形式的已定义类型级别名称,即类型族和数据族,它们混合了上述某些属性。数据族定义的名称与构造函数非常相似(它们是可插入的,可以部分应用),而类型族定义的名称与别名非常相似(不能保证它们是单射的,不能部分应用)。主要区别在于它们可以执行“类型级模式匹配”,其中有多个定义适用于不同情况。完整的描述可能不适合StackOverflow的答案,但是the manual describes them以及指向讨论它们的多篇论文的链接。