Using context bound and implicitly in Scala

Posted on August 9, 2016

Context bound is a syntactic sugar for implicit argument:

def f[A : B](a: A) = g(a) // where g requires an implicit value of type B[A]

which is de-sugared to

def f[A](a: A)(implicit ev: B[A]) = ...

(Scala Documentation)

So far so good, but on the same page there is another very common example in the library:

def f[A : Ordering](a: A, b: A) = implicitly[Ordering[A]].compare(a, b)

so one can imagine it’s a syntactic sugar for

def f[A](a: A, b: A)(implicit ev: Ordering[A]) = ev.compare(a, b)

Let’s look closer at de-sugared versions:

// Type bound with implicitly
def f(a: Object, b: Object, evidence$2: scala.math.Ordering): Int = 
  scala.Predef.implicitly(evidence$2).$asInstanceOf[math.Ordering]().compare(a, b);

// Implicit argument
def f(a: Object, b: Object, ev: scala.math.Ordering): Int = ev.compare(a, b);

Well, I have to admit both versions are doing the same thing at the end, but if I’m writing a library method there is a difference. It’s even more obvious when looking at the bytecode:

 0: getstatic     #32
 3: aload_3
 4: invokevirtual #36
 7: checkcast     #14
10: aload_1
11: aload_2
12: invokeinterface #40,  3
17: ireturn

vs

0: aload_3
1: aload_1
2: aload_2
3: invokeinterface #40,  3
8: ireturn

So whenever performance is an issue I would avoid using the pattern with implicitly.