Scalaz Equal

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.

Getting Scalaz

Add the following line to your build.sbt file:

1
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.2.14"


NOTE:

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> import Scalaz._
import Scalaz._

Ammonite

Start the REPL by executing the amm command. Then, execute the following:

1
2
3
4
5
6
@ import $ivy.`org.scalaz::scalaz-core:7.2.14`
import $ivy.$
@ import scalaz._
import scalaz._
@ import Scalaz._
import Scalaz._

Equality Checking

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: type mismatch;
found : String("1")
required: Int
val res4 = 1 === "1"
^
Compilation Failed

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: type mismatch;
found : String("1")
required: Int
val res5 = 1 =/= "1"
^
Compilation Failed

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
def equal(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?

1
2
3
4
5
6
7
8
9
10
11
@ case class Person(id: Int, name: String)
defined class Person
@ implicit object PersonEquals extends Equal[Person] {
// we are using Scalaz === internally
def equal(a1: Person, a2: Person): Boolean = a1.id === a2.id && a1.name === a2.name
}
defined object PersonEquals
@ Person(1, "John") === Person(2, "Jane")
res7: Boolean = false
@ Person(1, "John") =/= Person(2, "Jane")
res8: Boolean = true

Conclusion

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.