So, I have been searching documentation about the main difference between parametric polymorphism and adhoc-polymorphism, but I still have some doubts.
For instance, methods like head in Collections, are clearly parametric polymorphism, since the code used to obtain the head in a List[Int] is the same as in any other List.
List[T] {
def head: T = this match {
case x :: xs => x
case Nil => throw new RuntimeException("Head of empty List.")
}
}
(Not sure if that's the actual implementation of head, but it doesn't matter)
On the other hand, type classes are considered adhoc-polymorphism. Since we can provide different implementations, bounded to the types.
trait Expression[T] {
def evaluate(expr: T): Int
}
object ExpressionEvaluator {
def evaluate[T: Expression](value: T): Int = implicitly[Expression[T]].evaluate(value)
}
implicit val intExpression: Expression[Int] = new Expression[Int] {
override def evaluate(expr: Int): Int = expr
}
ExpressionEvaluator.evaluate(5)
// 5
In the middle, we have methods like filter, that are parametrized, but we can provide different implementations, by providing different functions.
List(1,2,3).filter(_ % 2 == 0)
// List(2)
Are methods like filter, map, etc, considered ad-hoc polymorphism? Why or why not?
The method
filteronLists is an example of parametric polymorphism. The signature isIt works in exactly the same way for all types
A. Since it can be parameterized by any typeA, it's ordinary parametric polymorphism.Methods like
mapmake use of both types of polymorphism simultaneously.Full signature of
mapis:This method relies on the presence of an implicit value (the CBF-gizmo), therefore it's ad-hoc polymorphism. However, some of the implicit methods that provide the CBFs of the right type are actually themselves parametrically polymorphic in types
AandB. Therefore, unless the compiler manages to find some very special ad-hoc construct like anCanBuildFrom[List[String], Int, BitSet]in the implicit scope, it will sooner or later fall back to something likeTherefore, I think one could say that it's a kind-of "hybrid parametric-ad-hoc polymorphism" that first attempts to find the most appropriate ad-hoc type class
CanBuildFrom[List[A], B, That]in the implicit scope, but eventually falls back to ordinary parametric polymorphism, and returns a one-fits-allCanBuildFrom[List[A], B, List[B]]-solution that is parametrically polymorphic in bothAandB.