I've seen lots of partial definitions of "True Polymorphism", for example here and here but nowhere have I been able to find a clear example of the difference with two concrete examples.
I understand that overloading the +
operator is some form of polymorphism and that it is implemented differently in Haskell and C++. Can someone show precisely what the difference is with examples in both languages?
The term you are looking for is "parametric polymorphism", which is different from "ad-hoc polymorphism".
An example of parametric polymorphism is in the type signature for Nothing
:
Nothing :: Maybe a
The a
in the type could be any conceivable type, since Nothing
inhabits all Maybe
s. We say that a
is parametrically polymorphic because it can be any type.
Now consider this type:
Just 1 :: (Num b) => Maybe b
This time the b
cannot be any type: it can only be a type that is an instance of Num
. We say that b
is ad-hoc polymorphic because it can be any member of a set of types, given by the instances of the Num
class.
So, to recap:
Parametric polymorphism: Can be any type
Ad-hoc polymorphism: Constrained by a type-class
There are three types of polymorphism you'll encounter frequently ( with C++ and Haskell examples ).
Parametric polymorphism in functional languages is a feature of type systems where the type of a function is an expression quantified over type variables. The input types constrain free parameters in signature which determines the output type. For example the map function takes a function as its first argument which determines the type of the input list and the output list.
map :: (a -> b) -> [a] -> [b]
In type theory parlance the signature is often written:
∀ a. ∀ b. (a -> b) -> [a] -> [b]
C++ can achieve the effects of parametric polymorphism through templates, but is in my opinion very brittle ( i.e. leads to vague compile errors ) and lacks the formalism found in found functional languages:
template <class T>
T add(T a, T b) {
return a+b;
}
Ad-hoc polymorphism is when functions with the same name act differently when "viewed" with different type signatures. In Haskell this is expressed with type classes. The type a
in signature for (+)
is bounded to types which implement the Num
type class.
(+) :: Num a => a -> a -> a
class Num a where
(+) :: a -> a -> a
instance Num Int where
(+) = plusInt
Subtype polymorphism. Not present in Haskell but other languages ( Scala, ML ) have subtype polymorphism. In object oriented languages this is usually a language feature where different object instances implement a method or property calls with the same name and are dispatched depending on the semantics of the object model. In C++ for example:
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");
}
};
The thing to remember about polymorphism is to separate the core idea from the implementation. For example, ad-hoc polymorphism is not the same as type-classes, it's just one expression of it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With