Scalaz and Typeclasses

What is Scalaz ?

In this video, we will investigate one of the more intriguing, difficult, but useful library in the Scala world: Scalaz. But before all, let’s try to understand what is really, this library.

Scalaz is a library of typeclasses

Scalaz is actually a large collection of typeclasses.

Based on the Haskell typeclass library in Scala

To be more precise, it is a rewrite in Scala of the more complete typeclass library in existence: the Haskell library. I understand this answer however can actually lead to more questions.

What is a typeclass then?

So, let’s try to answer to the more obvious question: what is a typeclass?

A typeclass in Scala is a pattern

A typeclass in Scala is a pattern. You must follow a common and well defined set of programming conventions to implement it.

In Haskell it is a language feature

Typeclasses originated in the language Haskell, where they are a feature of the language.

Scala is actually expressive enough to implement it.

In Scala, they are not a primitive feature of the language in itself, because Scala offers the needed features to easily implement the concept. In Scala, a typeclass is thus implemented as a pattern.

A typeclass extends arbitrary classes

The purpose of a typeclass is somewhat similar to an interface. A typeclass defines a common set of methods that can be used to extend existing classes.

But no modifications are needed to the class

However implementing an interface requires you extend a class and implement the methods. Typeclass methods instead can be applied to existing instances without explicitly modifying the type.

You just create typeclass instances to implement the methods

To implement a typeclass for a class, you need to create an instance of the typeclass. As we will see, typeclass instances provides the implementations for the methods defined in the typeclass.

Scalaz typeclasses are backed by mathematics!

Furthermore, typeclasses as implemented in Scalaz, are actually backed by a mathematical theory: the Category Theory. Typeclass names follows equivalent concepts in this theory.

You must verify that your implementation satisfy the laws

Because of their mathematical background, each typeclass in Scalaz requires the instances satisfy a set of laws. When you implement your instance , you need to verify the implementation satifies those laws.

A typeclass example

Let’s see what we talked about so far. I will show an example of a typeclass.

1
2
3
4
val n = 20
val m = 10
val s = "Andrea"
val t = "Beatrice"
n: Int = 20
m: Int = 10
s: String = "Andrea"
t: String = "Beatrice"

We want to compare numbers and strings, using an operator , that is not normally available for integers and strings.

1
n ?|? m
Compilation Failed
Main.scala:59: value ?|? is not a member of Int
n ?|? m
  ^

Of course, the application of such a method to numbers, fails.

1
s ?|? t
Compilation Failed
Main.scala:59: value ?|? is not a member of String
s ?|? t
  ^

The same holds true for strings.

Enter Scalaz

Now we can start to appreciate the power of Scalaz. First and before all we need to load the library.

1
load.ivy("org.scalaz" %% "scalaz-core" % "7.1.4")

With this command, we load the library for command line usage. We are using here the ammonite scala shell. We will use this shell in this presentation. You may use a different one, or just write the code in your development environment and compile it.

1
import scalaz._, Scalaz._
import scalaz._, Scalaz._

Once you have the library available in your environment, you need to import it. Actually, it is all what is needed to enable Scalaz. The idiom of importing a package, then importing an object with the same name, is very common in Scala.

Using Scalaz

Now, we can start to write code using Scalaz. Your code does not need any other modification.

Using ?|? from Order typeclass

We are going to use a comparison operator, which belongs to the Order typeclass.

1
2
3
n
m
n ?|? m
res3_0: Int = 20
res3_1: Int = 10
res3_2: Ordering = GT

As you can see, without changing any code, the new operator is available for integers and can compare numerically integers. It will return an Ordering value. In this case the result means greater.

1
2
3
s
t
s ?|? t
res4_0: String = "Andrea"
res4_1: String = "Beatrice"
res4_2: Ordering = LT

The same operator works also for strings. Now, we have a new operator, which works comparing strings alphabetically.

Let’s consider a custom class

Let’s see what happens, when we define a custom class, as follows.

1
case class Person(name: String, age: Int)
defined class Person

Here is a case class defining a person with a name and an age.

1
2
val a = Person("Andrea", 20)
val b = Person("Beatrice", 10)
a: Person = Person("Andrea", 20)
b: Person = Person("Beatrice", 10)

We created two instances, Andrea twenty years old, and Beatrice, a teen ager.

1
a ?|? b
Compilation Failed
Main.scala:1772: value ?|? is not a member of cmd6.INSTANCE.$ref$cmd5.Person
a ?|? b
  ^

However, the two instances are not comparable, using our new operator. We have to define how we want to handle the comparison of persons.

Defining a typeclass instance

A typeclass defines generically methods and operators. A typeclass instance defines how the typeclass behaves for a specific type.

1
implicit val personOrder = Order.order[Person](_.name cmp _.name)
personOrder: Order[Person] = scalaz.Order$$anon$12@61f086a7

Here, we defined a new instance of the typeclass. We do not go yet into the details. The key is you need to pass a function, which defines how we want to compare two persons. We decided to compare persons using their name.

1
a ?|? b
res8: Ordering = LT

As you can see, now two persons can be compared, and “Andrea” is lower than “Beatrice”, alphabetically speaking.

1
a max b
res9: Person = Person("Beatrice", 10)

Defining the ordering, you can use other methods in the Order typeclass. We have now a working definition for the max operator for Persons.

Changing our mind

We can change our mind and decide to order persons by their age.

1
implicit val personOrder = Order.order[Person](_.age cmp _.age)
personOrder: Order[Person] = scalaz.Order$$anon$12@7c7038bb

We redefine now the order with a new typeclass instance. This one is actually comparing persons looking at their ages.

1
2
a ?|? b
a max b
res11_0: Ordering = GT
res11_1: Person = Person("Andrea", 20)

As expected now Andrea is greater than Beatrice because he is older.

Where is the magic?

Let’s try to understand how a typeclass actually works. Since everything is based on implicits, the key is to discover how the implicits are expanded.

1
2
3
n ?|? m
// is actually
ToOrderOps[Int](n)(intInstance).?|?(m)

Using a compiler switch we can see how typeclasses are actually implemented. This is the code after implicits resolution. It is actually a two step process. We will explain this process in detail in the following.

1
2
3
a ?|? b
// is actually
ToOrderOps[Person](a)(personOrder).?|?(b)

Here is the code for the expansion of the custom typeclass instance. It is different because of the type, so it is using the typeclass instance we defined.

Step 1: wrap the value with an adapter class

1
2
ToOrderOps[Int](n)
ToOrderOps[Person](a)

The first step is to use an implicit conversion. Implicit conversions are invoked when no other options are available to the compiler. A single conversion function must match the types. The implicit conversion returns an instance of a wrapper class. It is this instance which provides the additional method we want.

Step 2: add the typeclass instance as a parameter

1
2
ToOrderOps[Int](n)(intInstance)
ToOrderOps[Person](a)(personOrder)

The actual implementation to use is provided by an implicit parameter, again matched by type. As you can see, for persons it uses the one we defined, while for integers it uses an instance provided with Scalaz.

Implementing our typeclass

To really understand how the typeclasses actually works, we will implement one.

1
2
3
4
// Java
interface Comparable<T> {
int compareTo(T o)
}

We are going to implement an equivalent of the Java comparable interface as a typeclass. The advantage is obviously we do not have to implement a subclass, in order to provide the functionality to existing types.

1
2
3
trait Comparable[T] {
def >?< (a: T, b: T): Int
}
defined trait Comparable

A typeclass in scala is implemented as a parametric trait. The trait have an abstract methods. You have to implement this method for each instance of the typeclass.

Our typeclass instances

Let’s define the implementations of the typeclass for integers and strings.

1
2
3
implicit object intComparable extends Comparable[Int] {
def >?<(a: Int, b: Int): Int = if (a < b) -1 else if (a > b) 1 else 0
}
defined object intComparable

This is the typeclass instance for integers. The implementation uses an if expression. The expression returns -1, 0, 1. It depends on the first value, if it is lower, equal or greater than the second.

1
2
3
implicit object stringComparable extends Comparable[String] {
def >?<(a: String, b: String): Int = a.compareTo(b)
}
defined object stringComparable

The implementation for strings instead, just uses the existing “compare to” method, already available.

Deriving the implementation

1 >?< 2

Now, let’s deduct the implementation, applying the same transformations we already saw before. We want to be able to apply the typeclasses when we simply use the operator with integers.

rewritten

1.>?<(2)

We apply now one basic scala rule, which transforms an operator application in a method invocation on the first member.

wrapped

toComparableOps(1)(intComparable).>?<(2)

Since integers do not have our method, an implicit conversion is applied. So we need a method able to wrap the integer in an object having our methods. Furthermore, the typeclass instance is applied as an implicit parameter.

expanded

intComparable.>?<(1, 2)

Now, what we really want is the code, able to expand the wrapped code to this actual invocation.

Putting all together

The actual implementation is actually pretty easy but we need two elements: a wrapper class, and the implicit conversion method, which returns the wrapper.

1
2
3
class ComparableOps[T](self: T, impl: Comparable[T]) {
def >?<(b: T) = impl.>?<(self, b)
}
defined class ComparableOps

The wrapper is a class, taking two parameters: the object we want to wrap, and the typeclass instance. Note that, while in the trait the method takes two parameters, in the wrapper class it takes only one, and store the first one as a field of the class.

1
2
3
implicit def toComparableOps[T](t: T)
(implicit ev: Comparable[T]) =
new ComparableOps(t, ev)
defined function toComparableOps

This is the implicit conversion method. It takes the object to wrap, the implicit typeclass instance, then construct the wrapper. The wrapper actually allows to invoke the typeclass instance methods.

Testing the implementation

Let’s verify if what we did works.

1
3 >?< 2
res17: Int = 1

The typeclass instance is correctly applied to integer.

1
"alpha" >?< "beta"
res18: Int = -1

Also the typeclass works for strings.

1
2
3
val a = Person("Andrea", 20)
val b = Person("Beatrice", 10)
a >?< b
Compilation Failed
Main.scala:1813: value >?< is not a member of cmd19.this.$ref$cmd5.Person
a >?< b
  ^

The typeclass does not work for persons untl we provide an implementation.

1
2
3
implicit object personComparable extends Comparable[Person] {
def >?<(a: Person, b: Person): Int = a.name.compareTo(b.name)
}
defined object personComparable

So here is the implementation. We choose to compare persons by alphabetical order of their name.

1
a >?< b
res20: Int = -1

As you can see here, now the implementation can compare persons. All we did was to add an implementation for its type, the rest is applied automatically as long as we follows the typeclass pattern.