OCaml中的多态性——ad hoc、参数化、包含/亚型



我在理解不同类型的多态性方面有问题,特别是在OCaml方面。我知道多态性允许OCaml中的多种类型表示为'a,但我不明白不同类型的多态性是什么。
如果有人能用相对低级的语言给我一个解释,那就太棒了!ad hoc, parametric, inclusion/subtyping

这是一个近似

Ad-hoc多态性通常是指能够用不同的类型声明相同的名称(通常是一个函数),例如SML中的+ : int -> int -> int+ : float -> float -> float。这些是不同的函数,它们可以以完全不同的方式运行,但编译器或解释器会根据上下文选择合适的函数。我想不出OCaml中有任何特殊多态性的实例。然而,它在c++和Java中很常见。

参数多态性是指单个函数可以使用任何类型的实参,因为不需要查看该实参的结构。例如,cons : 'a -> 'a list -> 'a list可以将任何类型的值v附加到相同类型的值列表中,因为v的结构(布局)是什么,或者它支持什么操作,对cons来说都无关紧要。在C语言中,cons不需要对指针"解引用",也不需要对v执行任何针对v实际类型的操作。注意,与ad-hoc多态性不同,cons 以相同的方式对所有类型起作用。因此,在某种程度上,参数多态性和特设多态性是彼此的自然"对立面"。参数多态性是OCaml中绝大多数多态性实例的原因。

子类型多态性是指当需要使用u类型的值时,可以使用t类型的值。这可能是因为类型t支持类型u的所有操作,或者因为t的结构可以用在需要u的地方。这方面的例子将是子类化(也许总线可以在车辆可以使用的地方使用),或多态变体(您可以在'A | 'B | 'C预期的地方使用'A | 'B)。

按注释编辑

但是请注意,必须在OCaml中显式地请求子类型。例如,如果您有一个函数f : u -> int,并且您希望将其应用于v : t,其中tu的子类型,则必须编写f (v :> u)(v :> u)语法是类型强制转换。

OCaml还支持行多态性,这是带约束的参数多态性的一种形式。如果f代替f : #u -> int(对象类型)或f : [< u] -> int(多态变体),#u/[< u]语法表示类型变量,类似于'a,但只能用u的各自"子类型"替换(在限制意义上,它们可以分别支持更多字段/更少构造函数)。然后,您可以在没有强制转换的情况下执行f v。对于许多涉及多态变量和对象的表达式,OCaml自动推断使用行多态性的类型,但是如果要创建签名,则必须显式地编写类型。

行多态性有更多的用法和注意事项。我忽略了实际的行变量和额外的语法,只描述了一些看起来像有界量化的东西(如Java泛型)。关于行多态、它的名称和/或它的形式化的更详细和准确的讨论可能最好留到单独的问题中讨论。

实际上我不认为这类问题特别适合Stack Overflow的优势。有整本关于类型的书。事实上,我推荐阅读Pierce的Types and Programming Languages,我发现这本书非常有启发性和令人愉快。

作为一个快速的回答(主要基于我对皮尔斯的记忆:-),以下是我对这些术语的看法。

参数多态性指的是包含自由变量的类型,其中的变量可以被任何类型替换。函数List.length具有这样的类型,因为它可以查找任何列表的长度(无论元素的类型是什么)。

# List.length;;
- : 'a list -> int = <fun>
OCaml的一个神奇之处在于它不仅支持这样的类型,它还推断出类型。给定一个函数定义,OCaml推断出函数的最一般的参数多态类型。

子类型是类型之间的关系。如果T的所有实例也是U的实例,则类型T是类型U的子类型(但不一定相反)。OCaml支持子类型,也就是说,它允许程序将T类型的值视为其超类型U的值。但是,程序员必须明确地要求。

# type ab = [ `A | `B ];;
type ab = [ `A | `B ]
# type abc = [`A | `B | `C ];;
type abc = [ `A | `B | `C ]
# let x : ab = `A;;
val x : ab = `A
# let y : abc = x;;
Error: This expression has type ab but an expression was expected
of type abc. The first variant type does not allow tag(s) `C
# let y : abc = (x :> abc);;
val y : abc = `A

在此例中,类型type ab是类型abc的子类型,而x的类型为ab。您可以使用x作为类型为abc的值,但必须使用:>类型操作符显式转换。

Ad-hoc多态性指的是由程序员为特定情况定义的多态性,而不是从基本原则派生出来的多态性。(或者至少这是我的意思,也许其他人用不同的术语。)一个可能的例子是OO继承层次结构,其中对象状态的实际类型不需要以任何方式关联,只要方法具有适当的关系。

关于ad-hoc多态性(IMHO)的关键观察是,它取决于程序员使其工作。因此,它并不总是有效。这里的其他类型的多态性,基于基本原理,实际上不会失败。在处理复杂系统时,这是一种令人欣慰的感觉。

标识是多态的:

# let ident x = x;;
val ident : 'a -> 'a = <fun>
# ident 1;;
- : int = 1
# ident "ok";;
- : string = "ok"
# ident [];;
- : 'a list = []
# ident List.length;;
- : '_a list -> int = <fun>
# ident ident;;
- : '_a -> '_a = <fun>

fold also:

# open List;;
# fold_left (+) 0 [1;2;3];;
- : int = 6
# fold_left (^) "" ["1";"2";"3"];;
- : string = "123"
# fold_left (fun a (x,y)  -> a+x*y) 0 [(1,2);(3,4);(5,6)];;
- : int = 44

最新更新