Nick says:
In this function
head
, it takes a list ofA
’s, and returns anA
. And it doesn’t matter what theA
is: It could beInt
s,String
s,Orange
s,Car
s, whatever. AnyA
would work, and the function is defined for everyA
that there can be.
def head[A](xs: List[A]): A = xs(0)
head(1 :: 2 :: Nil)
// res1: Int = 1
case class Car(make: String)
head(Car("Civic") :: Car("CR-V") :: Nil)
// res2: Car = Car(make = "Civic")
Haskell wiki says:
Parametric polymorphism refers to when the type of a value contains one or more (unconstrained) type variables, so that the value may adopt any type that results from substituting those variables with concrete types.
Let’s think of a function plus
that can add two values of type A
:
def plus[A](a1: A, a2: A): A = ???
Depending on the type A
, we need to provide different definition for what it means to add them.
One way to achieve this is through subtyping.
trait PlusIntf[A] {
def plus(a2: A): A
}
def plusBySubtype[A <: PlusIntf[A]](a1: A, a2: A): A = a1.plus(a2)
We can at least provide different definitions of plus
for A
.
But, this is not flexible since trait Plus
needs to be mixed in at the time of defining the datatype.
So it can’t work for Int
and String
.
The third approach in Scala is to provide an implicit conversion or implicit parameters for the trait.
trait CanPlus[A] {
def plus(a1: A, a2: A): A
}
def plus[A: CanPlus](a1: A, a2: A): A = implicitly[CanPlus[A]].plus(a1, a2)
This is truely ad-hoc in the sense that
A
Int
) without access to its source code
The last point makes Scala’s ad-hoc polymorphism more powerful than that of Haskell. More on this topic can be found at Debasish Ghosh @debasishg’s Scala Implicits : Type Classes Here I Come.
Let’s look into plus
function in more detail.