patternMinor
Is subtype polymorphism a kind of ad hoc polymorphism?
Viewed 0 times
kindsubtypepolymorphismhoc
Problem
"Ad-hoc polymorphism is obtained when a function works, or appears to work, on several different types (which may not exhibit a common structure) and may behave in unrelated ways for each type." – Strachey 1967
Subtype polymorphism seems to fit this description, albeit usually with late binding on the type that dictates the function's behavior. In Java, for example, the
But when I poke around online, I usually find people making a sharp distinction between subtype polymorphism and ad hoc polymorphism; they are treated as wholly different beasts. Would it be correct to say that subtype polymorphism is a kind of ad hoc polymorphism? If not, why not?
Subtype polymorphism seems to fit this description, albeit usually with late binding on the type that dictates the function's behavior. In Java, for example, the
toString function works on any object at all, but has many wholly distinct implementations which are distinguished from one another based on the runtime type of the object; i.e., any class can override it and create a new ad hoc definition.But when I poke around online, I usually find people making a sharp distinction between subtype polymorphism and ad hoc polymorphism; they are treated as wholly different beasts. Would it be correct to say that subtype polymorphism is a kind of ad hoc polymorphism? If not, why not?
Solution
Keep in mind that, in PL lingo, the term "polymorphism" is used for different notions.
In OOP, polymorphism usually means subtype polymorphism. If we have a function
In functional programming, we often do not have subtypes. Still functions can have a universally quantified type such as
In some OOP languages, a similar feature can be found with the name of "generic function", e.g.
There is a terminology mismatch here since OOP decided to use the "polymorphism" term to refer to the subtyping one, hence it needed a new term for "parametric polymorphism".
Anyway, in parametric polymorphism, values of the "unknown" quantified types
In real-world languages, sometimes parametric polymorphism is broken by some specific constructs in the language. Minor offenders are
When these are present, one still obtains a weaker form of parametric polymorphism. Worse offenders are
Indeed, consider this pseudo-Java code
This is the identity function on all types, except
What we get, instead, is just a function which can act on each type
In OOP, polymorphism usually means subtype polymorphism. If we have a function
foo(x: A) we can call foo with any object having a subtype of A. In this way, the function is defined only once but can operate on "many types" -- which justifies the usage of the word "polymorphism".In functional programming, we often do not have subtypes. Still functions can have a universally quantified type such as
bar : forall a b, a -> b -> a. This function can take two arguments of any types a and b (chosen by the caller), and return a value of type a. Hence, this is called a polymorphic function. In some OOP languages, a similar feature can be found with the name of "generic function", e.g.
A bar(A a, B b) in Java. We can call also this feature "parametric polymorphism".There is a terminology mismatch here since OOP decided to use the "polymorphism" term to refer to the subtyping one, hence it needed a new term for "parametric polymorphism".
Anyway, in parametric polymorphism, values of the "unknown" quantified types
a,b can not be concretely used (since we do not know what they are), but can be merely passed around. This is why bar above must be the projection of its first argument: the function has no other way to produce a return value of type a except to reuse its first argument. Similarly, baz : forall a, a->a must be the identity function.In real-world languages, sometimes parametric polymorphism is broken by some specific constructs in the language. Minor offenders are
- non termination: infinite loops / recursion allows to write a
baz : forall a, a->awhich never terminates, hence it differs from the identity function.
- exceptions / runtime errors: similar to non termination.
nullvalues: ifnullis a value of any typea, that can be returned bybazas well, making it differ from the identity.
When these are present, one still obtains a weaker form of parametric polymorphism. Worse offenders are
- checked type casts /
instanceof
- reflection
Indeed, consider this pseudo-Java code
baz(A x) {
if (x instanceof Integer)
return (x+1); // Some casting omitted to make this work
else
return x;
}This is the identity function on all types, except
Integer where it does something arbitrarily different. The function above is ugly: it does not handle the x argument as an argument of an unknown type. It actually checks the type of x at runtime and makes decisions on that. We no longer have parametric polymorphism here, since type A is not handled in a "uniform" way: the A=Integer is now "special".What we get, instead, is just a function which can act on each type
A (so it is still polymorphic) but which on each A can have a completely unrelated behavior. In a sense, it is just an unconstrained family of functions, indexed over a type $A$. This is what we call "ad hoc polymorphism".Code Snippets
<A> baz(A x) {
if (x instanceof Integer)
return (x+1); // Some casting omitted to make this work
else
return x;
}Context
StackExchange Computer Science Q#82485, answer score: 5
Revisions (0)
No revisions yet.