Haskell中的多态

一般地来说,一个值(这里的值也包括普通函数)是多态的,当它可以是多种类型的。Haskell原始支持的多态分为参数多态和ad-hoc多态。
在文章haskell学习笔记中提到了Parametric polymorphism、higher kinded polymorphism和higher rank polymorphism三个概念,我们将在本文中详细探讨。

参数多态和Ad-hoc多态

参数多态

参数多态(Parametric polymorphism)类似于其他语言中的泛型(模板),此时一个值的类型中包含多个(unconstrained) type variable。

1
2
id :: a -> a
id x = x

1
2
3
4
template <typename T>
auto id(T x){
return x;
}

在上面的代码中,a称为(unconstrained) type variable,它可以是任意的,这体现在没有=>这样typeclass的约束。因此这也隐含了对于不同的类型,多态值的行为都是一致的,这是因为parametrically polymorphic value本身对自己的type variable类型一无所知。

Ad-hoc多态

相对应地,Ad-hoc显得更随机应变。此时,这个多态值可以采用若干类型其中的一个,对于每一种不同类型,他们的行为都是不同的。例如对于(+),它对浮点数的处理和对整数的处理显然是不一样的。
特别地,typeclass中定义的函数也可以是类型多态或Ad-hoc多态的,这在文章haskell学习笔记的Ch3有详细探讨。

Rank N多态

Haskell中的Rank N Types模块实现了higher rank polymorphism的概念。
首先我们研究什么是Rank 1,

1
2
3
{-# LANGUAGE RankNTypes #-}

id :: forall a. a -> a

引入的forall显式说明id能够作用在任意的(universally quantified)类型上。

接下来是Rank 2,

1
2
3
type IdFunc = forall a. a -> a
id :: IdFunc
id x = x

在上面的代码中,我们为forall a. a -> a这样类型的函数创建了一个alias,称为IdFunc,然后我们定义了一个IdFunc类型的函数id。在这一步中,type variable a消失了。紧接着我们又定义someInt这个函数,这个函数是monomorphic的,虽然它接受一个polymorphic的参数。

1
2
someInt :: IdFunc -> Integer
someInt id' = id' 3

进一步看函数someInt,它要求我们去传入一个多态函数,但自己却是单态的。这样的函数在原始Haskell中是不允许的,支持它们会给类型推导带来困难。

从另一个角度查看这个问题,

1
a -> b -> a

上面的type signature隐含了ab这两个type variable是universally quantified的,因此可以被写作

1
forall a b. a -> b -> a

forall关键字可以被提到(->)的右边,或者(->)右边的forall可以提到前面(正如(->)是右结合的)。即下面的形式也是Rank-1的,并且与上面的写法都等价

1
forall a. a -> (forall b. b -> a)

但是在某一个(->)左边的forall不可以被提出。下面的语句中,后面的forall可以被提到最前面,因为它在(->)的右边;而前面的forall则不能提到最前面。

1
(forall a. a -> a) -> (forall b. b -> b)

我们发现在上面的语句中出现了两个层面的universal quantification,forall b能够被提到最前面,适用范围更广一点。这样的类型就是Rank-2的。

1
2
3
4
5
6
Prelude> f :: (forall a. a -> a) -> (forall b. b -> b)

<interactive>:3:15:
Illegal symbol '.' in type
Perhaps you intended to use RankNTypes or a similar language
extension to enable explicit-forall syntax: forall <tvs>. <type>

类型多态

在先前,我们讲到的“函数”都是关乎值的,而类型多态,也就是higher kinded polymorphic,可以看做类型和类型之间的函数。

子类多态

Haskell并没有提供子类多态的性质。子类多态实际上是雷子Protocol、Interface、Inherit这样的东西。

Reference

  1. https://ocharles.org.uk/blog/guest-posts/2014-12-18-rank-n-types.html
  2. https://www.stephanboyer.com/post/115/higher-rank-and-higher-kinded-types
  3. https://wiki.haskell.org/Rank-N_types
  4. https://wiki.haskell.org/Polymorphism