我已经看到了很多“真多态性”的部分定义,例如这里和这里,但我无法通过两个具体示例找到一个明确的差异示例。
我知道重载+
运算符是某种形式的多态性,并且它在 Haskell 和 C++ 中的实现方式不同。有人可以准确地说明两种语言的示例有什么区别吗?
您正在寻找的术语是“参数多态性”,它不同于“临时多态性”。
参数多态的一个例子是在类型签名中Nothing
:
Nothing :: Maybe a
a
in 类型可以是任何可以想象的类型,因为它存在于所有Nothing
sMaybe
中。我们说它a
是参数多态的,因为它可以是任何类型。
现在考虑这种类型:
Just 1 :: (Num b) => Maybe b
这次b
不能是任何类型:它只能是 的实例类型Num
。我们说它b
是临时多态的,因为它可以是由Num
类的实例给出的一组类型的任何成员。
所以,回顾一下:
参数多态性:可以是任何类型
Ad-hoc 多态性:受类型类约束
您会经常遇到三种类型的多态性(使用 C++ 和 Haskell 示例)。
函数式语言中的参数多态性是类型系统的一个特征,其中函数的类型是对类型变量进行量化的表达式。输入类型约束签名中的自由参数,它决定了输出类型。例如,map 函数将一个函数作为其第一个参数,该参数确定输入列表和输出列表的类型。
map :: (a -> b) -> [a] -> [b]
用类型论的说法,签名通常写成:
∀ a. ∀ b. (a -> b) -> [a] -> [b]
C++ 可以通过模板实现参数多态的效果,但在我看来非常脆弱(即导致模糊的编译错误),缺乏找到的函数式语言中的形式主义:
template <class T>
T add(T a, T b) {
return a+b;
}
Ad-hoc 多态性是指具有相同名称的函数在使用不同类型签名“查看”时行为不同。在 Haskell 中,这是用类型类表示的。a
签名中的类型(+)
绑定到实现Num
类型类的类型。
(+) :: Num a => a -> a -> a
class Num a where
(+) :: a -> a -> a
instance Num Int where
(+) = plusInt
亚型多态性。Haskell 中不存在,但其他语言(Scala、ML)具有子类型多态性。在面向对象的语言中,这通常是一种语言特性,其中不同的对象实例实现同名的方法或属性调用,并根据对象模型的语义进行调度。以 C++ 为例:
class Animal {
public:
virtual void speak() = 0;
};
class Cat : public Animal {
public:
void speak() {
printf("Meow");
}
};
class Dog : public Animal {
public:
void speak() {
printf("Woof");
}
};
关于多态性要记住的事情是将核心思想与实现分开。例如,ad-hoc 多态与类型类不同,它只是它的一种表达方式。