Higher Kinded Type - 高阶类类型
Kind
维基百科:Kind (type theory)
“kind"中文翻译也是“类型”,这样就跟"type"有些混淆。下文仅将“type”翻译类型。
Kind是类型理论(Type Theory)中的定义。一个kind便是一个类型构造器的类型(the type of a type constructor),或者说是高阶类型类型操作符的类型(the type of a higher-order type operator)。
Kind System本质上是简单类型Lambda演算(simply typed lambda calculus)的“上一层”。定义一个原始类型(primitive type)(记作*/$*$、称之为“type”),它是任意数据类型(data type)的kind,不需要任何类型参数。
kind有个可能看上去有些莫名其妙的解释:“(数据)类型的类型”(type of a (data) type),但实际上它也有些元数指示的意思。从语法上讲,可以很自然地将多态类型视为类型构造器,因此非多态类型可以视为一个0元类型构造器/无参类型构造器,所有的无参类型构造器都是相同的、最简单的kind,便是*/$*$
高阶类型操作符在编程语言中并不常见,在大多数编程实践中,kind用于区分数据类型和用于实现参数化多态的构造器类型(types of constructors which are used to implement parametric polymorphism)。在类型系统使得用户能编码实现参数化多态的编程语言中(如C++ Haskell Scala),或明或暗地有kind的概念存在。
Examples
*/$*$是所有数据类型的kind,读作"type”,也可看作0元类型构造器/无参类型构造器,在当前上下文中也称为恰当类型(proper type),通常也包含函数式编程语言中的函数类型。例如Int/Bool/List<Int>/Map<String, List<Double>>的kind都是** -> */$* \rightarrow *$是一元类型构造器(unary type constructor)的kind。例List类型的kind便是* -> *,需要给出一个类型参数Int才能得到恰当类型List<Int>* -> * -> */$* \rightarrow * \rightarrow *$是二元类型构造器(binary type constructor,curry方式实现)的kind。例如Tuple便是二元类型构造器;函数类型构造器->也属此类(注:对->应用的结果是函数类型,函数类型kind是*)(* -> *) -> */$(* \rightarrow *) \rightarrow *$是从一元类型构造器到恰当类型的高阶类型操作符的kind
| Values | Types | Kinds |
|---|---|---|
1 |
Int |
* |
[1,2,3] |
List<Int> |
* |
(1, "") |
Tuple<Int, String> |
* |
fun i -> i % 2 == 0 |
Int -> Bool |
* |
| - | List |
* -> * |
| - | Tuple |
* -> * -> * |
| - | -> |
* -> * -> * |
从左向右提升抽象层次
Kind In Haskell
Haskell的Kind系统中,$K = * | K \rightarrow K$,它描述了两条规则:*/$*$是所有数据类型的kind;* -> */$* \rightarrow *$是一元类型构造器的kind,接受一个kind然后给出一个kind。
确实有值的类型才是算作具体类型,或者叫恰当类型。例如,4是Int类型的值、[1, 2, 3]是[Int]类型的值、fun i -> i % 2 == 0是Int -> Bool类型的值、fun x -> fun y -> x % y == 0是Int -> Int -> Bool类型的值。这些类型的kind都是*。
一个类型构造器接受一个或多个类型参数,当接受了足够的类型参数之后便产生了一个新类型(类型构造支持基于currying的偏应用)。例如,[]/List接受一个类型参数,这个类型参数指出了内部元素的类型,因此[Int]/[Bool]/[[Int]]都是对于[]的正确应用,[]的kind是* -> *,Int/Bool/[Int]的kind是*,将之应用到[]便得到[Int]/[Bool]/[[Int]],这些结果类型的kind是*。同理,二元组类型构造器(,)的kind是* -> * -> *,三元组类型构造器(,,)的kind是* -> * -> * -> *
HKT in Programming
静态类型语言中,类型系统来保证值的使用安全性,kind系统是来保证类型的使用安全性。
主流编程语言中提供的一般是一阶参数化多态(first-order parametric polymorphism),这一功能一般称为泛型(generic)。泛型能对类型进行抽象并进行静态检查,但无法对类型构造器进行抽象,进而无法保证这方面类型安全(不支持HKT的话也写不出来,因为编译器不接受这样的代码,只能舍弃抽象,从而导致代码重复)。
|
|
以上代码用Scala简单演示了引入HKT带来的收益,确实减少了不必要的代码重复,而对于类型安全的保证也不会有任何损失。下面是个稍复杂些的例子,用Haskell和Scala对函子进行定义和实现,从而对HKT进行演示。
|
|
|
|
分析以上代码,定义函子Functor用到了参数f/F[_],该参数是个类型构造器,其kind便是* -> *,于是函子Functor的kind便是(* -> *) -> *,这里便是kind的“高阶”所在。此外要注意函数类型构造(->),对于其返回类型呈(协变)函子性,因此可以固定函数参数类型为r/R,然后可实现r -> ?/R => ?函子。Scala中需要显示指出类型构造器,Haskell可以通过对于参数的应用推断出这是个类型还是个类型构造器。
论文Generics of a Higher Kind解释了Scala对于HKT的设计及应用演示,论文中给出了一个更具实用意义的例子,通过对Iterable基础类库的设计和实现,演示了HKT存在的必要性(上文已简要演示)。此外也简要解释了Scala的“implicit”设计理念,该设计以另一种方式提供了Haskell的Type Class提供的特设多态(ad-hoc polymorphism)功能。此外,Scala的Kind System中,kind还附带着类型的上下界、可变性的约束信息,以此来确保程序的正确性。
F#尚不支持HKT,需要CLR改进才能支持这一功能,对功能的讨论在:Simulate higher-kinded polymorphism。Robert Kuzelj的Higher Kinded Types in F#系列博客演示了HKT存在的必要性以及在F#中如何对其进行模拟。
Higher Kinded Types in typescript博客介绍了在TypeScript中对HKT功能进行模拟。
Rust对HKT的似乎也开始支持,高阶类型 Higher Kinded Type对此进行了简单介绍,看评论似乎不是HKT完整支持。