Frank Zinner bio photo

Frank Zinner

I'm a passionated senior software developer ...

Twitter Facebook LinkedIn Github Xing

Working with the scalaz library

Scalaz is a pure funtional library used to code pure functional in scala. Scalaz can be optained via github.com where it’s hosted. You simply include it via sbt as a dependency like:

libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.1.1"

For maven users - it’s also hosted on maven central. Just search for “scalaz”.

Then you use it with simply two imports:

import scalaz._
import Scalaz._

Getting started:

A => Identity[A]
M[A] => MA[M[_], A]
M[A, B] => MAB[M[_,_], A, B]
// Wrappers: 
OptionW[A], ListW[A] // etc.
Equal[T] // Type-safe equality

Monoid Type Class:

trait Semigroup[M] {
	def append(a: M, b: M): M
}

trait Monoid[M] extends Semigroup[M] {
	val zero: M
}

Lots of monoids in scalaz will be used with the |+| operator.

Monoids compose

(Monoid[A], Monoid[B]) => Monoid[(A, B)]

or for functions

Monoid[B] => Monoid[A => B]

like

f |+| g = (x => f(x) |+| g(x))

also

Monoid[A] => Monoid[Option[A]]` 

like

Some("abc") |+| Some("def")

results in

res: Option[String] = Some(abcdef)

Works also for maps and is useful for adding/sum up trees of data:

Monoid[V] => Monoid[Map[K,V]]

like

Map("a" -> 2, "b" -> 1) |+| Map("a" -> 3, "c" -> 4)
res: Map[String, Int] = Map(a -> 5, c -> 4, b -> 1)

The same code can be re-used for all monoids:

def sum[A: Monoid](as: List[A]): A = as.foldLeft(mzero)(_ |+| _)

Foldable

If there exist an implicit Foldable[M] and Monoid[A], then

M[A].sum: A
M[B].foldMap(B => A): A

this holds for Lists etc.

Examples:

List(1,2,3).asMA.sum returns Int = 6
List(some(1), some(4), none).asMA.sum returns Option[Int] = Some(5)
some(2) foldMap (_ + 1) returns Int = 3
List(3,4,5) foldMap multiplication returns scalaz.IntMultiplication = 60

Validation

Purely functional error handling

sealed trait Validation[+E, +A]
case class Success[+E, +A](a: A) extends Validation[E, A]
case class Fail[+E, +A](e: E) extends Validation[E, A]

Example:

def checkEmail(e: String): Validation[String, String] = {
	if (validEmail(e)) email.success
	else "Invalid email address".fail
}

Validation compose with map and flatMap:

def validateWebForm(name: String, email, String, phone: String): Validation[String, WebForm] =
for {
	e <- checkEmail(email);
	p <- checkPhone(phone)
} yield WebForm(name, e, p)

If the failure type is a Monoid, we can accumulate failures:

def validateWebForm(name: String, email: String, phone: String): Validation[String, WebForm] =
(checkEmail(email) |@| checkPhone(phone)) WebForm(name, _, _)

ValidateNil returns a List of failures.

List(Success(1), Success(2)).sequences returns List(1,2)

Applicative Functors

Is defined as trait:

trait Applicative[M[_]] {
	def apply[A, B](a: M[A], f: M[A => B]): M[B]
	def pure[A](a: A): M[A]
}
trait Traverse[T[_]] {
	def traverse[M[_]]: Applicative,A,B](a: T[A], f: A => M[B]): M[T[B]]
}
x.sequence = traverse(x, a => a)

Any M[A] can be composed with |@| if there exists Applicative[M] in implicit scope.

(Some(30) |@| Some(10) |@| Some(2)) { x, y, z => if (x > 20) y else z }

or

(List("foo", "bar") |@| List("baz", "qux")) { _ |@| _ } 
List[String] = List(foobaz, fooqux, barbaz, barqux)

can also be partially applied

(((_: Int) + 1) |@| ((_: Int) * 2)) { _ |+| _ } 
res12: Int => Int = <function1>

res12(4) Int = 13

Any F[G[A]] can be inverted to G[F[A]] if there exists Applicative[G] and Traverse[F].

Example:

List(some(1), some(2), some(3)).sequence 
Option[List[Int]] = Some(List(1,2,3))

and

Some(List(1,2,3)).sequence
List[Option[Int]] = List(Some(1), Some(2), Some(3))