Over a million developers have joined DZone.

Typeclass in Scala Libraries and the Compiler (Part 2)

DZone's Guide to

Typeclass in Scala Libraries and the Compiler (Part 2)

Get ready for a deep dive of how type class works in Scala libraries and the language's compiler to help achieve polymorphism.

· Java Zone ·
Free Resource

Take 60 minutes to understand the Power of the Actor Model with "Designing Reactive Systems: The Role Of Actors In Distributed Architecture". Brought to you in partnership with Lightbend.

Type class, strictly speaking, is a library feature instead of a compiler feature; they are used not only in Scala application design, but also in library design. The compiler wires pieces together through type level programming and implicits, eliminates object inheritance, and achieves polymorphism. It is an integrated part of the Scala language. In Part 1 of this series, we have discussed TypeTags and CanBuildFrom type classes. We continue this discussion in Part 2.

Generalized Type Constraint Type Classes

The Generalized type constraints <:<[A, B] and =:=[A,B] are infix types. The purpose of this type class is to inform the compiler to do type checking.

An instance of <:<[A, B], or its syntactic sugar A <:< B, is to witness that A is a subtype of B; while an instance of =:=[A, B], or its syntactic sugar A =:= B, witnesses that A and B are of the same type.

The <:<[A, B] type class is defined in Predef as follows:

sealed abstract class <: < [-From, +To] extends(From => To) with Serializable
private[this] final val singleton_ <: < = new <: < [Any, Any] {
    def apply(x: Any): Any = x
implicit def $conforms[A]: A <: < A = singleton_ <: < .asInstanceOf[A <: < A]

Pay attention to the implicit $conforms[A]: A <:< A, and the variance of the typeclass <:<[-From, +To]. Type parameter +To has a covariance annotation, which indicates a subtyping relationship between the class and its type parameter.

For class U[+A], if A is a subtype of B, then U[A] is a subtype of U[B]; the type parameter -From has a contravariance annotation, which indicates a subtyping relationship of: for class U[-A], if A is a subtype of B, then U[B] is a subtype of U[A]. Then look at the implicit $conforms[A]: A <:< A, and its single type parameter A. By its variance annotation, if A is a subtype of To and, meanwhile, a supertype of From, that makes From <: A <: To. This is exactly the conformance we are looking for:  From <: To, and $conforms[A]: A <:< A conforms to <:<[-From, +To].

$conforms[A]: <:<[A, A] references a private singleton instance singleton_<:<, and it is the only implicit within the scope that conforms to <:<[-From, +To] the compiler will find it during its implicit search. For instance:

class Animal
class Tiger extends Animal
class Cat extends Animal
def compareTypes[T, U](t: T, u: U)(implicit ev: T <:< U) = {
    println("type check successful")
val a = new Animal
val t = new Tiger
val c = new Cat
compareTypes(t, a) //compiling OK
compareTypes(a, t) //compiling Not OK
compareTypes(t, c) //compiling Not OK

An implicit parameter list is provided to function compareTypes. The type checking is performed at compiling time, the compiler will report error if type T is not a subtype of type U.

A typical use of this typeclass in scala library is toMap in various collection implementations:

def toMap[K, V](implicit ev: A <:< (K, V)): Map[K, V]

The type class A <:< (K, V) witnesses that the element of the collection is a tuple, otherwise the compiler will report an error.

Type Classes in the Scala Concurrency Model

Concurrency can be achieved via Future and Promise in Scala. A Future represents a computation value that may or may not yet available. A Promise can be used to complete a Future. Both Future and Promise must be run in an ExecutionContext, which provides an implementation of concurrency environment. A default ExecutionContext is provided in the Scala library, although a customized ExecutionContext can be also used.

Future API is defined as follows.

trait Future[+T] extends Awaitable[T] {
         def onSuccess[U](pf: PartialFunction[T, U])
                 (implicit executor: ExecutionContext): Unit = …
         def onFailure[U] (pf: PartialFunction[Throwable, U])
                 (implicit executor: ExecutionContext): Unit = …
         def onComplete[U](f: Try[T] => U)
                 (implicit executor: ExecutionContext): Unit

And in its companion object:

object Future {
         def apply[T](body: =>T)(implicit executor: ExecutionContext): Future[T] =…

Creating a Future starts an asynchronous operation on another thread. The ExecutionContext in the implicit parameter list provides the thread pool that the Future will use for Future instance creation, as well as various callbacks. ExecutionContext is a type class; to use the default ExecutionContext, import the implicit instance provided by the library;

import scala.concurrent.ExecutionContext.Implicits.global

Note that operating on Promise does not need an ExecutionContext until it has something to do with the promised Future. A promise can be made under any circumstance (without an ExecutionContext), because a promised Future can be completed with a value for Success or with an exception for Failure, handled in the Future callbacks:

val p = Promise[T]()
val f = p.future
f onSuccess { doSomething}
f onFailure {  doSomethingElse }

Another concurrent model, Akka Actors, works the same way. An ExecutionContext is made implicitly available from the Actor context:

class SomeActor extends Actor {
    implicit val ec: ExecutionContext = context.dispatcher

There is another interesting type class in Scala's concurrency model: Classifier. For instance, you can wait for a Future to complete in a 2-minute span:

Await.result(someFuture, 2 minute span)

Here, 2 minute span can be considered an internal DSL implemented in type class and implicits. To understand how it works, we trace backward from its use. 2 minute span is an infix operator notation of the function:


First, as minutes is a method of FiniteDuration, the Scala compiler finds an implicit conversion from Int to FiniteDuration:

implicit final class DurationInt(private val n: Int) extends AnyVal with DurationConversions {
     override protected def durationIn(unit: TimeUnit): FiniteDuration=Duration(n.toLong, unit)

Second, the minutes function is called with an implicit parameter of type class Classifier:

def minutes[C](c: C)(implicit ev: Classifier[C]): ev.R = ev.convert(minutes)

As we have passed in a parameter of the span type, the compiler found a spanConvert implicit object in Classifier’s companion object:

implicit object spanConvert extends Classifier[span.type] {
    type R = FiniteDuration
    def convert(d: FiniteDuration) = d

Here, span is a type introduced in package object duration that is able to participate in the type computation:

package object duration {
         object span

Here we have converted an Int (2) into a FiniteDuration object of 2 minutes.

Ordering Type Class

Scala uses the Ordering type class to sort a sequential collection. The Ordering trait extends Comparator, a Java functional interface, and implements its compare(T,T) method.

trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable

Ordering’s companion object in the Scala library contains many predefined implicit objects that deal with all subtypes of AnyVal, and some subtypes of AnyRef, such as String, BigInt, BigDecimal, Option, and Tuple2 to Tuple9. The predefined implicit objects override the comparison function

Most of the Ordering of subtypes of AnyVal are straightforward. Take Int, for instance.

We can obtain a sorted Seq:

val sortedSeq = Seq(1,3,2,5).sorted

As subtypes of AnyRef do not have a natural ordering, a functional Comparator shall be defined. This is an example of a retroactive extension. For instance:

case class Person(name:String, id:Int)
trait PersonOrdering extends Ordering[Person] {
    def compare(x: Person, y: Person) = x.name compareTo y.name
implicit object Person extends PersonOrdering

And we can obtain a sorted Person Sequence:

val persons:Seq[Person] = Seq(Person("Michael", 0), Person("Daniel", 1), Person("Jackie", 2)).sorted

Ordering also provides a handy function to return an instance by passing in an anonymous function that extracts key(s):

implicit def orderingByName[A <: Person]: Ordering[A] = Ordering.by(e => e.name)
def by[T, S](f: T => S)(implicit ord: Ordering[S]): Ordering[T] =
  fromLessThan((x, y) => ord.lt(f(x), f(y)))

The function by[T, S](f: T => S)(implicit ord: Ordering[S]) returns an Ordering object that requires an implicit parameter of (cmp: (T, T) => Boolean). Given the type of String, function lt(String, String)(x,y)<0 is called.

def by[T, S](f: T => S)(implicit ord: Ordering[S]): Ordering[T] =
    fromLessThan((x, y) => ord.lt(f(x), f(y)))


Type class is an important design pattern in Scala. It combines Scala’s implicits and type system to achieve ad hoc polymorphism and retroactive extension. Type class is an abstraction of a contextual environment, while concrete objects are obtained through an implicit search within this environment. In this article, we have discussed how this pattern is used in Scala libraries and the compiler. Readers can look into some popular Scala libraries such as Scalaz and Cats for more interesting uses.

Learn how the Actor model provides a simple but powerful way to design and implement reactive applications that can distribute work across clusters of cores and servers. Brought to you in partnership with Lightbend.

java ,scala ,functional programing ,typeclass ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}