In this post we’ll finally start using Scalaz. We’ll look at how to get Scalaz using sbt and look at how it provides us with type-safe equality checking.
I highly recommend Ammonite Scala REPL. It provides a lot of improvements over the standard REPL like being able to import library dependencies straight from the console. I’ll be using Ammonite REPL henceforth because it’ll help me keep the examples in the REPL. However, there isn’t any difference beyond how to get the dependencies.
Fire Up the REPL
No matter what your preferred REPL is, let’s get started.
Standard REPL
Start the REPL by executing sbt console. Then, execute the following:
1 2 3 4 5
scala> import scalaz._ import scalaz._
scala> importScalaz._ importScalaz._
Ammonite
Start the REPL by executing the amm command. Then, execute the following:
We can compare two values in Scala using == (double equals). The issue, however, is that it will let us compare unrelated types like a string with an integer and such a comparison would always yield false.
1 2
@ 1 == "1" res3: Boolean = false
The == operator that Scala provides is a null-safe comparison operator and not type-safe. What if we want type-safety, too? This is where Scalaz’s === (triple equals) comes in. It’ll complain when you try to compare unrelated types.
1 2 3 4 5 6 7
@ 1 === "1" cmd4.sc:1: typemismatch; found : String("1") required: Int val res4 = 1 === "1" ^ CompilationFailed
Similarly, we can check for inequalities. The Scala operator != is null-safe but not type-safe.
1 2
@ 1 != "1" res4: Boolean = true
Here’s the Scalaz way to check for inequality using =/= operator which is both type-safe and null-safe:
1 2 3 4 5 6 7
@ 1 =/= "1" cmd5.sc:1: typemismatch; found : String("1") required: Int val res5 = 1 =/= "1" ^ CompilationFailed
Under the Hoods
As always, there are type classes at play here. There is an Equal trait which provides an equal to check if the two values are equal and of the same type.
1
defequal(a1: F, a2: F): Boolean
Since all this magic is done using type classes, how about we put it to use and write code to compare two Person objects?
Scalaz === lets you check for equality in a type-safe way. More often than not, this is what you need. Trying to compare values of dissimilar types is usually not needed. Using === ensures that such comparisons lead to errors at compile-time instead of waiting for them to surface at run-time. This makes debugging much more efficient.
Polymorphism is a programming language feature that allows one interface to be used for a general class of actions.[1] Scalaz makes extensive use of ad-hoc polymorphism to provide its set of goodies. In this post I’ll cover ad-hoc polymorphism in detail and show you how you can implement ad-hoc polymorphism in Scala. I’ll also talk about parametric, and subtype polymorphism.
Parametric Polymorphism
In parametric polymorphism, the behavior of the function does not depend upon the type of the arguments passed to it. More formally[2],
Parametric polymorphism refers to when the type of a value contains one or more (unconstrained) type variables, so that the value may adopt any type that results from substituting those variables with concrete types.
Here, the argument xs has an unconstrained type A which can be anything and yet head would work. Then we call head with lists of concrete type Int, and String resulting in xs being of type List[Int] and List[String].
Subtype Polymorphism
Subtype polymorphism involves inheritance. More formally[3],
Subtype polymorphism is a form of type polymorphism in which a subtype is a data-type that is related to another data-type (the super-type) by some notion of substitutability.
As an example[4], consider a trait Plus which lets its subtype add itself with another of the same type.
The function plus[A <: Plus[A]] will work only with those arguments that are subtype of Plus. This is restrictive because for this to work, the trait needs to be mixed in at the time of defining whatever concrete type A represents.
Ad-hoc Polymorphism
In ad-hoc polymorphism the behavior of the function depends on what the type of the argument is. More formally[5],
Ad-hoc polymorphism refers to when a value is able to adopt any one of several types because it, or a value it uses, has been given a separate definition for each of those types.
The simplest way to do ad-hoc polymorphism is by overloading functions. For example, having plus(Int, Int), plus(Currency, Currency), etc. The other ways are by using type classes, and coercion.
Type Classes
I’ll rewrite the Plus[A] example using type classes:
Relating the above implementation to the formal definition, the implicit p was able to adopt one of CurrencyPlus or KilogramPlus depending on what arguments were passed. Thus the behavior of plus is polymorphic as its behavior comes from the definition of each of the types.
Coercion
The other way to implement ad-hoc polymorphism is coercion. Coercion refers to implicitly converting the argument into a type that the function expects. Here’s an example modeled around scala.runtime.RichInt:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
scala> classAwesomeInt(val self: Int) { | defabsolute: Int = math.abs(self) | } defined classAwesomeInt
The absolute method is defined in AwesomeInt but what we have is a plain old Int. This Int gets transformed into an AwesomeInt automagically because we have an implicit conversion within scope. The Int was coerced into an AwesomeInt.
So we see how ad-hoc polymorphism allows us to extend classes to whose code we don’t have access. We defined absolute to work with Int which comes from the Scala core library.
Higher-Kinded Types (HKT)
In the post where we generalized sum function, we generalized it to work with a List. What if we want to generalize it even further so that it can work with not just list but anything? Here’s what we want to achieve:
1 2 3 4 5
// we have this defsum[A](xs: List[A])(implicit m: Monoid[A]): A = ???
// we want this defsum[M[_], A](xs: M[A])(implicit m: Monoid[A], f: FoldLeft[M]): A = ???
The difference between the two is that the first version, although polymorphic over types for which we have monoids defined, is still heavily tied to List. The second version instead will work with type that is FoldLeft. We’ll expand upon the sum example[6]. We’ll need the monoids and we’ll need to add a FoldLeft to make sum more generic.
You might wonder what the whole point of using so much polymorphism is. The answer is that it lets you inject new functionality into existing libraries without having access to their source code. To quote Martin Odersky[7]:
There’s a fundamental difference between your own code and libraries of other people: You can change or extend your own code, but if you want to use some other libraries you have to take them as they are … Scala has implicit parameters and conversions. They can make existing libraries much more pleasant to deal with.
So, by using all the polymorphism techniques, we can implement the “pimp my library” pattern.[8]
The Pimp my Library Pattern suggests an approach for extending a library that nearly does everything that you need but just needs a little more. It assumes that you do not have source code for the library of interest.
Conclusion
The polymorphism capabilities provided by Scala allow Scalaz to provide its set of features. By using HKT we can write code that truly generalizes across multiple types. It might seem like a long-winded way to do something so simple but as codebase gets larger and larger, the features that Scalaz provides out-of-the-box really help in eliminating boilerplate code. We haven’t seen how to use Scalaz, yet. These posts serve as a foundation to understand how Scalaz does what it does.
A lot of what Scalaz does is made possible by using ad-hoc polymorphism, traits, and implicits. I’ll explain how this works by borrowing from Nick Partridge’s talk on Scalaz.
Motivating Example
Let’s begin with a simple sum function that adds together all the elements in a List[Int].
1 2
scala> defsum(xs: List[Int]): Int = xs.foldLeft(0)( _ + _ ) defined function sum
The sum function above works only with List[Int]. If we want to sum together a List[Double], List[Float], or a List[String], we’d need a new implementation of sum. Our goal is to make sum function general so it would work with any of these.
Step 1 - Monoid
The first step towards generalizing sum is by using a monoid. A monoid is an algebraic structure with a single associative binary operation and an identity element.[1]. Since we are working with a List[Int], let’s create an IntMonoid.
1 2 3 4 5 6 7 8
scala> objectIntMonoid{ defmempty: Int = 0 defmappend(a: Int, b: Int): Int = a + b } defined objectIntMonoid
scala> defsum(xs: List[Int]) = xs.foldLeft(IntMonoid.mempty)(IntMonoid.mappend) defined function sum
mempty is the identity or the zero value, and mappend is the binary operation which produces another Int i.e. another value in the set. These names come from Haskell[2].
Step 2 - Generalizing the Monoid
Next, we’ll generalize the monoid by creating a Monoid[A] so that IntMonoid is just a monoid on Int.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
scala> traitMonoid[A] { defmempty: A defmappend(a: A, b: A): A } defined traitMonoid
scala> objectIntMonoidextendsMonoid[Int] { defmempty: Int = 0 defmappend(a: Int, b: Int) = a + b } defined objectIntMonoid
scala> defsum[A](xs: List[A], m: Monoid[A]): A = xs.foldLeft(m.mempty)(m.mappend) defined function sum
scala> sum(List(1, 2, 3), IntMonoid) res3: Int = 6
What we’ve done is create a general-purpose sum function whose working depends upon which monoid is passed to it. Now we can very easily sum a List[String] or a List[Double] by adding a corresponding monoid.
Step 3 - Make the Monoid Implicit
Next, we’ll make the monoid an implicit parameter to our sum function. We’ll also package our IntMonoid into a Monoid companion object and make it implicit. The reason for doing this is how Scala compiler resolves implicit values; it’ll look for implicit values in its scope. So, we bring IntMonoid within scope by importing from the Monoid companion object.
scala> traitMonoid[A] { defmempty: A defmappend(a: A, b: A): A } defined traitMonoid
scala> objectMonoid{ implicitobjectIntMonoidextendsMonoid[Int] { defmempty: Int = 0 defmappend(a: Int, b: Int) = a + b } } defined objectMonoid
scala> importMonoid._ importMonoid._
scala> defsum[A](xs: List[A])(implicit m: Monoid[A]): A = xs.foldLeft(m.mempty)(m.mappend) defined function sum
scala> sum(List(1, 2, 3)) res4: Int = 6
So, what we’ve done is create a general-purpose sum function that works as long as there is a corresponding implicit monoid within scope. This is made possible by using ad-hoc polymorphism. I’ll cover ad-hoc polymorphism briefly in this post and defer providing a detailed explanation for a later post.
Ad-hoc Polymorphism
Ad-hoc polymorphism is a type of polymorphism in which polymorphic functions are invoked depending on the different argument types. One way to implement ad-hoc polymorphism that we already know about is function overloading where a different “version” of the function is invoked depending on the type of arguments. This is what we’ve done in the post where there is only a single function but an implementation is provided for different types. In other words, sum only knows how to be invoked but the behavior is provided by monoids. Another way to implement ad-hoc polymorphism is coercion where the argument is converted into a type which the function expects.
So, by using ad-hoc polymorphism, Scalaz is able to provide general-purpose functions over existing types. Ad-hoc polymorphism is flexible in the sense that it lets you extend even those classes for which you do not have access to the source code i.e. classes from other libraries, etc.
I’ve been using Scalaz for a while now and I remember not having any guides that provide a gentle introduction. It was a lot of scouring around, reading source code, and watching tech talks that gave me the understanding that I have now. This series of posts is intended at filling that gap by providing simple, step-by-step tutorials that will help you get productive quickly without compromising on the functional programming concepts.
What is Scalaz?
The documentation for Scalaz (pronounced Scala-zee or Scala-zed) states:
Scalaz is a Scala library for functional programming. It provides purely functional data structures to complement those from the Scala standard library. It defines a set of foundational type classes (e.g. Functor, Monad) and corresponding instances for a large number of data structures.
In a nutshell, Scalaz aims to do three things:
Provide new datatypes that are not present in the core Scala library
Provide new operations on existing types a.k.a. pimp the library
Provide general-purpose functions so that you don't have to re-write them
I’ll provide a quick example of each of these without going into any details.