类型类

计算机科学中,类型类(type class),是支持特设多态类型系统构造。这是通过向参数多态类型的类型变量增加约束完成的。这种约束典型的涉及到一个类型类T和一个类型变量a,并意味着a所能实例化的类型,其成员必须支持关联于T的重载运算。

类型类首先在Haskell中实现,当时Philip Wadler和Stephen Blott提出它,作为对Standard MLeqtype的扩展[1][2],并且最初构想为以本原方式实现重载算术及等式算符的一种途径[3][4]。 对比于Standard ML的“eqtypes”,在Haskell中通过使用类型类重载等式算符,不要求编译器前端或底层类型系统的广泛修改[5]

自从它们创立之后,已经发现了类型类的很多其他应用。

概述

定义类型类需要通过规定一组函数或约束名字,它们分别具有同在的类型,它们对属于这个类的所有类型都必须存在。在Haskell中,类型可以被参数化,意图包含允许等式的类型一个类型类Eq,可以如下这样声明:

class Eq a where
  (==) :: a -> a -> Bool
  (/=) :: a -> a -> Bool
  x /= y  =  not (x == y)
  x == y  =  not (x /= y)

这里的a是类型类Eq的一个实例,并且a定义的函数签名,针对了2个函数(等式和不等式函数),它们每个都接受2个类型a的实际参数并返回一个布尔值。这个声明可以读作:“类型a属于类型类Eq,如果在其上定义了有适当类型的,叫做(==)(/=)的的函数。”

编程者可以使任何类型t成为给定类型类C 的成员,通过使用“实例声明” 为特定类型t实现所有的C方法。如果编程者定义一个新的记录数据类型Foo,可以接着使这个新类型成为Eq的实例,通过以任何他们认为合适的方式,提供在类型Foo的值之上的等式函数和不等式函数,例如:

data Foo = Foo {x :: Integer, str :: String}
 
instance Eq Foo where
   (Foo x1 str1) == (Foo x2 str2) = (x1 == x2) && (str1 == str2)

编程者可以接着如下这样定义函数elem,它确定一个元素是否在一个列表之中:

elem :: Eq a => a -> [a] -> Bool
elem y []     = False
elem y (x:xs) = (x == y) || elem y xs

函数elem的类型a -> [a] -> Bool具有上下文Eq a,将参数多态函数的类型变量a可包括的类型,约束为属于Eq类型类的那些类型。Haskell中的 => 可表示“类型类继承”或“类型约束”。函数elem可应用于属于Eg类型类的任何类型的元素和这个类型的列表二者之上。

注意类型类不同于面向对象编程语言中的。特别是,Eq不是一个类型:没有类型Eq的值这种东西。类型变量a种类(kind),在最新的GHC发行中叫做Type[6]。意味着Eq的种类是:

Eq :: Type -> Constraint

高种类多态

类型类不但接受种类Type的类型变量,它可以接受任何种类的类型变量。这些有更高种类的类型类有时叫做构造子类,这里的构造子指称的是类型构造子比如Maybe,而非数据构造子比如Just。例如单子Monad

class Monad m where
  return :: a -> m a
  (>>=)  :: m a -> (a -> m b) -> m b

m应用到一个类型变量上的事实,指出了它有着种类Type -> Type,就是说它接受一个类型并返回一个类型,因而Monad的种类是:

Monad :: (Type -> Type) -> Constraint

多参数类型类

类型类允许多个类型参数,因此类型类可以被看作在类型上的关系[7]。例如,在GHC标准库中,类IArray表达一个通用不可变数组接口。在这个类中,类型约束IArray a e意味着a是一个数组类型,它包含类型e的元素。例如,在多态上的这种限制被用来实现开箱数组类型。

函数依赖

在Haskell中,类型类已经被精制为允许编程者声明在类型参数之间的函数依赖,这个概念受到关系数据库理论中的函数依赖的启发[8][9]。就是说,编程者可以断言对类型参数的某个子集的给定指派,唯一性的确定余下的类型参数。例如,承载一个类型s的状态参数的,一个一般性的单子m,满足类型类约束Monad.State s m。在这个约束中,有函数依赖m -> s。这意味着对于类型类Monad.State的一个给定单子m,从m能访问到的状态类型是被唯一性确定的。这会辅助编译器进行类型推论,还能辅助编程者进行类型导向编程。

Simon Peyton-Jones因其复杂性而反对在Haskell中介入函数依赖[10]

运算符重载的其他方式

Standard ML中,“等式类型”的机制粗略的对应于Haskell的内建类型类Eq,但是所有等式算符都自动的由编译器导出。编程者的过程控制局限于,指定在一个结构中哪些类型成员是等式类型,和在多态类型中哪些类型变量涉及等式类型。

SML和OCaml的模块和函子可以扮演类似于Haskell的类型类的角色,原则区别在于类型推论的角色,它使得类型类适合于特设多态[11]OCaml的面向对象子集是某种程度上与类型类有可比性的另一种方式。

有关概念

用于重载数据的一个类似概念(实现于GHC中)是类型家族[12]

Clean中的类型类与Haskell相似,但是有稍微不同的语法。

Rust支持trait,这是具有一致性的有限形式的类型类[13]

Mercury有类型类,却不完全同于Haskell。

Scala中,类型类是编程惯例,可以用现存语言特征比如隐式参数来实现,本身不是独立的语言特征。由于它们在Scala中的这种实现方式,在有歧义的情况下,有可能显式的指定哪种类型类实例用作在代码中特定位置上的类型。但是,这不必然有益处,因为有歧义的类型类实例是易于出错的。

证明辅助Coq在新近版本也支持类型类。不像在平常编程语言中那样,在Coq中,在类型类定义内陈述的任何类型类定律(比如单子定律),在使用它们之前,必须对每个类型类实例进行数学证明。

参见

引用

  1. Morris, John. (PDF). 2013 [2021-02-08]. (原始内容存档 (PDF)于2020-11-04).
  2. Wadler, Philip. . October 1988.
  3. Kaes, Stefan. . . March 1988. doi:10.1007/3-540-19027-9_9.
  4. Wadler, Philip; Stephen Blott. . . January 1989 [2021-02-08]. (原始内容存档于2016-03-11).
  5. Appel, Andrew; David MacQueen. . . June 1991 [2021-02-08]. (原始内容存档于2008-05-09).
  6. Type 页面存档备份,存于 from Data.Kind appeared in version 8 of the Glasgow Haskell Compiler
  7. Haskell' page MultiParamTypeClasses 页面存档备份,存于.
  8. Mark Jones. Type Classes with Functional Dependencies 页面存档备份,存于. From Proc. 9th European Symposium on Programming. March, 2000.
  9. Haskell' page FunctionalDependencies 页面存档备份,存于.
  10. . [2021-02-10]. (原始内容存档于2014-12-25).
  11. Dreyer, Derek; Robert Harper; Manuel M.T. Chakravarty. . April 2006 [2021-03-01]. (原始内容存档于2007-05-19). |book-title=被忽略 (帮助)
  12. . [2021-02-10]. (原始内容存档于2014-10-28).
  13. . [2021-02-10]. (原始内容存档于2020-11-12).

外部链接

維基教科書中的相關電子:类和类型
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.