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))