First of all, this post is long overdue. When I’ve been reading up on Scala, even without going into the nitty-gritty, I’ve found that my knowledge was lacking. I was full of misconceptions and mental crutches from OOP. What I knew from the OOP-land was just a tip of the iceberg.
This post will be mostly about typeclasses, and I’ll try to summarize what I’ve learned about polymorphism in Scala. The motivation for this is simple - I’ve begun to ponder what would I say given a simple interview question: “Could you explain polymorphism?”.
Previously my reaction to this would be to describe inheritance, virtual methods and maybe going into v-tables. Now my response wold be more akin to “What kind of polymorphism?”.
Subtype polymorphism was the only type of polymorphism I was aware of. Subtype polymorphism is the bread and butter of OOP. The typical introduction commonly shows it by using analogies to real life objects. Not being original I’ll use animals performing various sounds. I know using classes to mimic real life objects isn’t a very good idea (as seen in the circle-ellipse problem), but bear with me for the sake of example.
The gist of subtype polymorphism is that we can abstract away some computation
(returning a string) depending on what that object is. A Cat
is a Animal
and can substitute for it.
The previously mentioned v-table is the usual implementation of subtype
polymorphism. A class stores a table of function pointers to the implementations
of functions. Calling the makeSound
method on a Cat
object would first find
the address of the makeSound
method (i.e. it would point Cat::makeSound
using “C++ terminology”).
Whether a particular implementation uses v-tables is beside the point, really. The design should just allow for dynamic dispatch: what method is called depends on the runtime type of the object.
is generic programming. But I didn’t know it was called parametric polymorphism in the functional-programming world.
With contrast to subtype polymorphism parametric polymorphism lets us define computations/data structures that can abstract over any type (or a subset) while writing it.
The way I think about it, a generic function/data structure doesn’t serve as an abstraction by itself it’s a “template” for future use. One cool thing about it is that generic methods usually don’t care about the particular type - it will be specified at some future time. Generic data types take the principle of least knowledge up to eleven.
Example time, a simple generic class:
The type of the Box will be defined at some point in the future:
I’ll try to tackle generic programming in Scala seriously in another post. For now, this will do.
Was ad-hoc polymorphism. Ad-hoc polymorphism allows us to abstract a computation over some subtype like parametric polymorphism. Unlike parametric polymorphism the type of the argument is important because it’s used to dispatch to a concrete implementation. Ad-hoc polymorphism can be though of as a mechanism for function overloading.
In contrast to subtype polymorphism ad-hoc polymorphism “is done” at compile time - I’ll show an example of this at the end.
Quoting after wikipedia a typeclass (or a type class) is a construct that supports ad-hoc polymorphism. In Haskell a type class is a language construct, but scala can get away with using mechanisms it’s got already: implicit classes.
I’ve written something about implicits here. You can check it out, as a means of introduction.
So how can we implement type classes in Scala?
Lets forget about implicits for a sec, and get back to the animal example.
Suppose we wanted to add a method that returns the name of offspring of an
animal. We could do it by providing an parametrized offspringName
operation
Instead of mixing in the trait, we define a ‘helper method’ that uses the
OffspringName
trait to give the result:
Now we can define concrete implementations for the OffspringName
operation
Now this works:
And because the types don’t match this does not:
As can be expected the actual implementation of the type class can be provided by an implicit argument like so:
This only works for implicits that are in scope, defining a new Animal
class
won’t magically provide an implementation
Of course we can add it as before. And that’s what’s really cool about typeclasses. We can add new functionality to old code without modifying it.
While in subtype polymorphism dispatch is done through the runtime type of an object, typeclasses dispatch on the compile time type. See:
Scala provides additional syntactic sugar for the implicit argument called view bounds. As an alternative to:
We can say:
As a side note the implicit parameter is called an evidence
- we can interpret
this as is there an evidence in the implicit scope that the type T supports the
OffspringName
“operation”.
Another trick is to have an implicit conversion providing a suffix method call: