Using the parametric version gives
- More information to the users of the function
- Constrains the number of programs you can write (free bug checking)
As a random example, suppose we have a method which calculates the roots of a quadratic equation
int solve(int a, int b, int c) {
// My 7th grade math teacher is laughing somewhere
}
And then you want it to work on other sorts of number like things besides int
. You can write something like
Num solve(Num a, Num b, Num c){
...
}
The issue is that this doesn't say what you want it to. It says
Give me any 3 things that are number like (not necessarily in the same way)
and I'll give you back some sort of number
We can't do something like int sol = solve(a, b, c)
if a
, b
, and c
are int
s because we don't know that the method is going to return an int
in the end! This leads to some awkward dancing with downcasting and praying if we want to use the solution in a larger expression.
Inside the function, someone might hand us a float, a bigint, and degrees and we'd have to add and multiply them together. We'd like to statically reject this because the operations between these 3 classes is going to be gibberish. Degrees are mod 360 so it won't be the case that a.plus(b) = b.plus(a)
and similar hilarities will arise.
If we use the parametric polymorphism with subtyping we can rule all this out because our type actually says what we mean
<T : Num> T solve(T a, T b, T c)
Or in words "If you give me some type which is a number, I can solve equations with those coefficients".
This comes up in a lot of other places as well. Another good source of examples are functions which abstract over some sort of container, ala reverse
, sort
, map
, etc.
ref
value type parameters, might actually modify the value type.