亚型多态性:
在 Java、C# 或 C++ 等编程语言中,您有一组控制多态性的子类型规则。例如,在面向对象的编程语言中,如果您的类型 A 是类型 B 的超类型;那么无论A出现在哪里,您都可以通过B,对吗?
例如,如果你有一个哺乳动物类型,而狗和猫是哺乳动物的子类型,那么无论哺乳动物出现在哪里,你都可以传递狗或猫。
您可以使用数据类型和构造函数在 SML 中实现相同的概念。例如:
datatype mammal = Dog of String | Cat of String
然后,如果您有一个接收哺乳动物的函数,例如:
fun walk(m: mammal) = ...
然后你可以传递 Dog 或 Cat,因为它们是哺乳动物的构造函数。例如:
walk(Dog("Fido"));
walk(Cat("Zoe"));
所以这就是 SML 实现类似于我们所知的面向对象语言中的子类型多态性的方式。
临时多态性:
强制
实际的混淆点可能是 Java、C# 和 C++ 等语言通常具有自动强制类型转换。例如,在 Java 中,int可以自动强制转换为long,float可以自动强制转换为double。因此,我可以有一个接受双精度的函数,并且可以传递整数。有人将这些自动强制称为 ad-hoc 多态性。
这种形式的多态性在 SML 中不存在。在这些情况下,您被迫手动强制或将一种类型转换为另一种类型。
fun calc(r: real) = r
您不能使用整数调用它,为此您必须先转换它:
calc(Real.fromInt(10));
因此,如您所见,SML 中不存在这种特殊的多态性。您必须手动进行强制转换/转换/强制。
函数重载
另一种形式的 ad-hoc 多态性是我们在 Java、C# 和 C++ 等语言中所说的方法重载。同样,在 SML 中没有这样的东西。您可以定义两个不同名称的不同函数,但不能使用相同的函数(相同名称)接收不同的参数或参数类型。
不要将函数或方法重载的概念与您在示例中使用的概念混淆,后者只是函数的模式匹配。这是这样的合成糖:
fun len xs =
if null xs then 0
else 1 + len(tl xs)
参数多态性:
最后,SML 提供了参数多态性,这与泛型在 Java 和 C# 中所做的非常相似,我理解这有点类似于 C++ 中的模板。
所以,例如,你可以有一个像
datatype 'a list = Empty | Cons of 'a * 'a list
在这样的类型中,'a 代表任何类型。因此这是一种多态类型。因此,我可以使用相同的类型来定义整数列表或字符串列表:
val listOfString = Cons("Obi-wan", Empty);
或整数列表
val numbers = Cons(1, Empty);
或哺乳动物列表:
val pets = Cons(Cat("Milo", Cons(Dog("Bentley"), Empty)));
这与您可以对 SML 列表执行的操作相同,它也具有参数多态性:
您可以定义许多“不同类型”的列表:
val listOfString = "Yoda"::"Anakin"::"Luke"::[]
val listOfIntegers 1::2::3::4::[]
val listOfMammals = Cat("Misingo")::Dog("Fido")::Cat("Dexter")::Dog("Tank")::[]
在同样的意义上,我们可以在函数中有参数多态性,就像在下面的例子中我们有一个恒等函数:
fun id x = x
x 的类型是 'a,这基本上意味着您可以将其替换为您想要的任何类型,例如
id("hello");
id(35);
id(Dog("Diesel"));
id(Cat("Milo"));
因此,如您所见,结合所有这些不同形式的多态性,您应该能够实现您在其他静态类型语言中所做的相同事情。