我在理解不同类型的多态性时遇到问题,特别是关于 OCaml。我知道多态性允许在 OCaml 中表示为 'a 的多种类型,但我不明白不同类型的多态性是什么。
如果有人可以用相对低级的语言给我一个解释,那就太棒了!ad hoc、参数化、包含/子类型化
3 回答
这是一个近似值。
Ad-hoc 多态性通常是指能够用不同的类型声明相同的名称(通常是一个函数),例如+ : int -> int -> int
在+ : float -> float -> float
SML 中。这些是不同的函数,它们的行为方式完全不同,但编译器或解释器会根据上下文选择合适的函数。我想不出 OCaml 中的任何临时多态性实例。然而,它在 C++ 和 Java 中很常见。
参数多态性是指单个函数可以处理任何类型的参数,因为它不尝试查看该参数的结构。例如,cons : 'a -> 'a list -> 'a list
能够将v
任何类型的值添加到相同类型的值列表中,因为它cons
的结构(布局)v
是什么或它支持什么操作并不重要。在 C 术语中,cons
不需要“取消引用”指针,或对其执行任何v
特定于实际类型的操作v
。请注意,与 ad-hoc 多态性不同,cons
有对所有类型采取相同的方式。因此,参数多态性和临时多态性在某种程度上是彼此天然的“对立面”。参数多态性是 OCaml 中绝大多数多态性实例的原因。
子类型多态性是指您可以在预期类型值的t
地方使用类型值。u
这可能是因为 typet
支持 type 的所有操作u
,或者因为t
的结构可以在u
预期的地方使用。这方面的例子是子类化(也许可以在任何车辆可以使用的地方使用总线)或多态变体(您可以'A | 'B
在'A | 'B | 'C
预期的地方使用)。
编辑每条评论
但是请注意,必须在 OCaml 中明确请求子类型。例如,如果你有一个函数f : u -> int
,并且你想将它应用到v : t
wheret
是 的子类型u
,你必须编写f (v :> u)
. (v :> u)
语法是类型强制。
OCaml 还支持行多态,这是一种带约束的参数多态。如果f
是f : #u -> int
(对于对象类型)或f : [< u] -> int
(对于多态变体),则/语法#u
表示[< u]
一个类型变量,类似于/less 构造函数)。然后,您可以在没有强制的情况下进行操作。OCaml 会自动为许多涉及多态变体和对象的表达式推断使用行多态性的类型,但如果要创建签名,则必须显式编写类型。'a
u
f v
行多态性有更多的用法和考虑。我忽略了实际的行变量和附加语法,只描述了一些看起来像有界量化的东西(如在 Java 泛型中)。对行多态性、它的名称和/或其形式的更详细和准确的讨论可能最好留给单独的问题。
我实际上并不认为这种问题特别适合 Stack Overflow 的优势。有很多关于类型的书。事实上,我建议阅读Pierce 的Types and Programming Languages,我发现它非常有启发性和令人愉快。
作为一个快速的答案(主要基于我从 Pierce 中记得的内容:-),这是我对这些术语的看法。
参数多态性是指其中包含自由变量的类型,其中变量可以被任何类型替换。该函数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
在此示例中,类型 typeab
是 type 的子类型abc
,并且x
具有 type ab
。您可以将x
值用作 type abc
,但您必须使用:>
类型运算符显式转换。
Ad-hoc 多态性是指由程序员针对特定情况定义的多态性,而不是从基本原则派生的多态性。(或者至少这就是我的意思,也许其他人使用不同的术语。)一个可能的例子是 OO 继承层次结构,其中对象状态的实际类型不需要以任何方式相关,只要方法有适当的关系。
关于临时多态性(恕我直言)的关键观察是由程序员来使其工作。因此,它并不总是有效。这里的其他类型的多态性,基于基本原理,实际上不能不工作。在处理复杂系统时,这是一种令人欣慰的感觉。
ident 是多态的:
# 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>
也折叠:
# 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