一旦引入一个类型变量,就会出现一个奇怪的特性叫做 parametricity (http://en.wikipedia.org/wiki/Parametricity )。这个特性表明,函数将会以一种统一的行为作用于所有的类型 。我们来研究下:
// head :: [a] -> a
注意看 head
,可以看到它接受 [a]
返回 a
。我们除了知道参数是个数组
,其他的一概不知;所以函数的功能就只限于操作这个数组上。在它对 a
一无所知的情况下,它可能对 a
做什么操作呢?换句话说,a
告诉我们它不是一个特定
的类型,这意味着它可以是任意
类型;那么我们的函数对每一个 可能的类型的操作都必须保持统一。这就是 parametricity 的含义。要让我们来猜测 head
的实现的话,唯一合理的推断就是它返回数组的第一个,或者最后一个,或者某个随机的元素;当然,head
这个命名应该能给我们一些线索。
再看一个例子:
// reverse :: [a] -> [a]
仅从类型签名来看,reverse
可能的目的是什么?再次强调,它不能对 a
做任何特定的事情。它不能把 a
变成另一个类型,或者引入一个 b
;这都是不可能的。那它可以排序么?答案是不能,没有足够的信息让它去为每一个可能的类型排序。它能重新排列么?可以的,我觉得它可以,但它必须以一种可预料的方式达成目标。另外,它也有可能删除或者重复某一个元素。重点是,不管在哪种情况下,类型 a
的多态性(polymorphism)都会大幅缩小 reverse
函数可能的行为的范围。
这种“可能性范围的缩小”(narrowing of possibility)允许我们利用类似 Hoogle 这样的类型签名搜索引擎去搜索我们想要的函数。类型签名所能包含的信息量真的非常大。