In this article, we will take a look at adhoc polymorphism
, implicits
and pimping
in Scala.
Polymorphism
Polymorphism == Many forms
, in software, polymorphism denotes providing a single interface
to entities of different types
(or) use generic/abstract symbols which can represent multiple different types. Before we look into adhoc polymorphism
we will look at two other types of polymorphism
Subtype Polymorphism
As the name suggests, Subtype polymorphism
is a form of type polymorphism in which a subtype
is a datatype that is related to another datatype (the supertype
) by some notion of substitutability. It states that, if a method
can operate on a supertype
it means that the same method will be able to operate on all the subtypes
of the given supertype. A common example would be if we have a method called process
which takes Shape
parameter, we should be able to pass any class which implements Shape
, for eg: Circle
, Square
, Rectangle
etc., and be able to invoke the function say draw()
exposed by Shape
interface.

As we can see in the above picture, we are able to pass different implementations of Shape
interface. The key takeaway here is the instance method which needs to be called is determined during run time
as opposed to compile time
. Hence this is also known as run time polymorphism
. We can implement subtype polymorphism
in many languages like C++
, Java
and C++
uses something called vtable
to call the right instance method during runtime.
Parametric polymorphism
In the case of parametric polymorphism
we use symbols
which stand for any type. The best example that we can quote is Generics in Java
. All the collection classes that we have in java.util
package like List<E>
, Set<E>
, Map<K,V>
etc., are parametrized so that it can hold any value. For example, if we can look at the put
function of Map<K,V>
we can see that it takes any value of type K
and any value of type V
.
V put(K key, V value)
Type safety is checked during compile
time so that developers will get lots of confidence that the code will not blow up during runtime in production. Since we are generalizing over any type, the logic will be totally agnostic of the type, which means we will not be doing anything special for any type. This means most of the logic will write itself due to the abstract nature of using symbols
. Parametric polymorphism
naturally fits in implementing data structures like List
, Set
, Map
etc., since their implementation is very generic and stays the same for all types. One way to think about parametric polymorphism in the context of Java Generics
is to visualize a box which holds something but we don’t care what is actually present inside the box but provide generic operations to operate across all types.
Adhoc polymorphism
Adhoc polymorphism is an extremely powerful type of polymorphism. It allows us to define a common interface for an arbitrary set of individually specified types. They help us to write code which are formally and precisely defined and hence they are extremely general. In Haskell, we implement adhoc polymorphism
using typeclasses
. In Scala
, we use trait
which captures the typeclass interface and various implementations
of that interface for different concrete types. The canonical example to understand Adhoc polymorphism
is to look at a very generic structure from functional programming
called Monoid
.
Monoid
In abstract algebra, which is a branch in mathematics, defines monoid as a set equipped with a associated binary operation
and an identity element
. For example, Integer addition
is a monoid with identity element as 0
1 + 0 = 1 // Left Identity
0 + 1 = 1 // Right Identity
1 + (2 + 3) == (1 + 2) + 3 //Associativity
Formally, a monoid needs to satisfy identity laws
(left
and right
) and associativity laws
.
-- Identity laws (Left and Right)
x <> mempty = x
mempty <> x = x
-- Associativity
(x <> y) <> z = x <> (y <> z)
Likewise Integer multiplication
, string concatenation
etc., form a monoid. The following code demonstrates the implementation of Monoid typeclass
, integer addition instance
and its corresponding usage in Scala,
trait Monoid[A] {
def combine(x: A, y: A): A
def empty: A
}
object Monoid {
val intAddition = new Monoid[Int] {
def combine(x: Int, y: Int): Int = x + y
def empty: Int = 0
}
}
object MonoidalOperation {
def monoidalCombineOperation[A](a: A, b: A, monoid: Monoid[A]): A = monoid.combine(a, b)
def monoidalEmptyElement[A](monoid: Monoid[A]): A = monoid.empty
}
To invoke monoidalCombineOperation
and monoidalEmptyElement
we can do the following:
MonoidalOperation.monoidalCombineOperation(2, 3, Monoid.intAddition)
MonoidalOperation.monoidalEmptyElement(Monoid.intAddition)
Typeclass and types
In the above implementation, we have a Monoid
trait which describes 2 generic
methods called combine
and empty
for type A
which needs to follow the identity laws
(left
and right
) and associativity laws
which are formally
defined. Any type instance which we write for Monoid
should strictly obey the laws put forth for that given typeclass
. The best part is, when we invoke monoidalCombineOperation
and monoidalEmptyElement
, compiler
will make sure that all the types are aligned and matching properly, else it will throw a compilation
error.
There are a wide variety of highly general purpose typeclasses
which defines its corresponding laws
and the type instances which are implemented for those typeclasses, strictly obeys these laws. Some of the examples are:

Implicits
If we look at the below code:
MonoidalOperation.monoidalCombineOperation(2, 3, Monoid.intAddition)
MonoidalOperation.monoidalEmptyElement(Monoid.intAddition)
for Int
type, what if we want one default type instance which is Monoid.intAddition
to be picked up by the compiler? If the compiler makes sure A
and Monoid[A]
are in alignment, how about the compiler automatically pass our default Monoid[A]
if present for a given value A
. This auto-magic way of compiler passing a given parameter is achieved via implicits
through a mechanism called implicit resolution
. We need to make the below changes to make this happen:


Now we can simply pass the Int
values and Scala compiler will implicitly
pass the Monoid[Int]
instance intAddition
to MonoidalOperation.monoidalCombineOperation
and MonoidalOperation.monoidalEmptyElement
since intAddition
is the only default instance of Monoid[Int]
available. If there were more than one instance, then we would get a compilation error.
MonoidalOperation.monoidalCombineOperation(2, 3)
MonoidalOperation.monoidalEmptyElement
Pimping
As we saw before, we achieve adhoc polymorphism
by defining a common interface for an arbitrary set of individually specified types. How about we expose those interface methods directly on the type itself? The challenge is, if those types are coming from a util
package (or) from a 3rd party library how do we go about enriching those types with the interface methods as we don’t own the source code. We can achieve it through a mechanism using Scala Pimping
.
In the above Monoidal Integer Addition
example, we would like to call combine
operation using |+|
syntax on the Int
type itself, as shown below:
2 |+| 3
Using the power of implicits
we can achieve it using the following snippet of code:

We can invoke the function |+|
via pimping by calling it directly on the Int
type using the below syntax:
import MonoidSyntax._
2 |+| 3
Summary
Adhoc polymorphism
is a powerful type of polymorphism, using which we can write highly generic functions with defined laws which all the type instances needs to obey. From this blog post, we can clearly see how Adhoc polymorphism
is implemented so elegantly in Scala using implicits
and pimping
and how all the 3 concepts are inter-twined to write software which are highly generic, extensible, verifiable and also at the same time statically type checked during compile time.