| | |

Scalaz for Dummies

By Stefan Bleibinhaus on
Scalaz for Dummies

Introduction

Scalaz is one of those things that everyone is talking about, but many teams are unsure if they actually want to use it. The question is often, “should we use Scalaz?” but we think the question should actually be “how should we use Scalaz?” 

Scalaz is an exhaustive standard library extension for Scala with a strong emphasis on functional programming and type correctness. Many parts of it are strongly inspired by Haskell and the theoretical concepts of functional programming. The library is known for pushing the abstraction barrier quite far, which gives it its reputation as a complex library (which is true!), but some concepts from it are actually quite easy, instantly usable, and very handy in programming Scala.

There are many tutorials about Scalaz explaining the theoretical concepts behind it, like Monads, Functors, Applicatives, Type Classes and the like. Learning Scalaz is a great resource on this, although a bit outdated sometimes. Instead of delving into these concepts, we are going to explain how we use Scalaz at Bench and describe the subset we are currently using with code examples.

Within the code examples, we assume that the whole scalaz world was imported:

import scalaz._, Scalaz._

 

Tagging

Tagging makes creating new types super easy. It uses the @@ symbol to "tag" an existing type as another type (in other words, it creates a new type). So String @@ Text should be read as "String tagged with Text".

For a practical example, we may tag a String with the Id tag so that we never accidentally pass any String as an Id.

object Tags {
 sealed class Id
}
type Id = String @@ Tags.Id
def Id(s: String) = s.asInstanceOf[Id]
The @@ is just a type (or more concretely a type alias). Symbolic types that take two parameters can be written in the infix position so String @@ Id is the same as @@[String, Id].
So here the type alias Id is actually an alias to String @@ Tags.Id, which expands to @@[String, Tags.Id], which in turn expands to String with Tagged[Id].

Tags can be used to:

  • Ensure type safety of APIs or data structures - it’s often better to use a tagged type than multiple String/Int or other primitive types
  • "Fake" a new type for type-classes (see below)

 

Typeclasses

Typeclasses are a mechanism for ad-hoc compile time polymorphism. A good blog post about type classes is Polymorphism and Typeclasses in Scala.

The important thing to note about typeclasses is that they "extend" the types at compile time. In scala this is implemented through implicits. There are two parts to a typeclass - the definition and the evidence (implementation) - similar to how in sub-type polymorphism there are interfaces and implementations.

In contrast to sub-type polymorphism:

  • Each class may have multiple implementations of type-classes, which are then imported into scope
  • We can make a typeclass out of classes that we do not have the source of (or that we do not want to touch)

 

Equal[A] typeclass

Like any other Scala shop, we use a lot of different type classes at Bench. The ones we use the most from Scalaz are Equal, Order and Enum. As an example of one of them, we’ll describe the Scalaz Equal typeclass, which provides a === type-safe equals method (and a type-safe =/= non-equals method).
Type safety means that === is only defined for types that provide explicit evidence of equality. So 1 === "1" won't compile (no evidence of Equals[Any]). This is different to the == behavior of Scala’s Any, which at best will only give a warning during compile time.

This means that for any types that we define, we must provide our own evidence of equality.

Fortunately, this is quite easy:

case class MyCaseClass(foo: Int, bar: String)

implicit val myCaseClassHasEquals: Equal[MyCaseClass] = new Equal[A] {
   def equal(a1: MyCaseClass, a2: MyCaseClass): Boolean = a1 == a2
}

// or use a helper method that derives equal from universal equality of Any
implicit val myCaseClassHasEquals: Equal[MyCaseClass] = Equal.equalA

 

Options

Scalaz does not implement its own Option format (yet) but adds some cool methods to help you work with Scala Options. 

Scalaz adds the some and none[A] methods defined on Any. They are used like this:
val x: Option[Int] = 1.some
val y: Option[Int] = none[Int]
These methods are nice because they always return Option[A], which helps the type interferencer. Compare:
1.some.fold {
  none[Int]
} {
  1.some
}

with

 Some(1) fold {
     None: Option[Int]
 }
     Some(1)
 }
 
Without the Option[Int], it would result in the following compile error:
error: type mismatch;
 found : Some[Int]
 required: Int => None.type

 

\/ (Disjunction or “the other Either”)

\/[A, B] can be understood as a nicer version of Scala’s Either - mostly because it has utility methods defined in it and you do not need to play around with projections to get things done.
\/ is right biased, which means that all operations like map, flatMap, etc. work on the "right" part of the type, which by convention is the success part. A \/ B can also be swapped to B \/ A by using .swap.

Creating this is easy and most commonly you will find yourself using:

scala> 1.right[String] 
res1: scalaz.\/[String,Int] = \/-(1)

scala> "Nope".left[Int] 
res2: scalaz.\/[String,Int] = -\/("Nope")
Where -\/ and \/- correspond to left and right values.
A handy factory method is fromTryCatch:
scala> \/.fromTryCatch { "1234".toInt }
res3: scalaz.\/[Throwable,Int] = \/-(1234)

This can be used as a drop in replacement for Scala’s Try.

 

Validation

Validation[A, B] is similar to \/[A, B] but isn't a Monad so it can not flatMap. On the other hand, it can compose aggregating errors, which is useful to give detailed error descriptions.

Take a look at this motivating example:

val v1: Validation[Throwable, Int] = 1.success[Throwable]
val v2: Validation[Throwable, Int] = (new RuntimeException).failure[Int]
val v3: Validation[Throwable, Int] = Validation.fromTryCatch("c".toInt)
We can aggregate all the validations to a ValidationNel (Nel stands for non empty list) and use sequenceU (defined on Traversable) to get:
val result: ValidationNel[Throwable, List[Int]] =
  List(v1, v2,v3).map(_.toValidationNel).sequenceU
If all validations in the list succeeded, this would expand to return a Success[List[Int]] if not (as is the case here) this expands to Failure[NonEmptyList[Throwable]]. Using fold on the result we can get either the success or the failure case.

 

Conclusion

This article showed some useful and at the same time simple parts of Scalaz – hopefully it motivates you to try it out on your own. As mentioned in the introduction, we believe that Scalaz can help you to use Scala even more effectively and is a valuable asset to improve your code quality.

Scalaz is a really comprehensive addition and we advise you to discuss which parts you want to use with your team before implementing anything. As much as Scalaz can improve your code, it can also be used to make your code almost unreadable and therefore inaccessible for the untrained.

At Bench we enjoy working with Scalaz, finding useful features, and discussing them within our team. We wish you the same fun we had in exploring Scalaz for your organization and hope this article was a good starting point for that!