Exploring Scalaz
Join the DZone community and get the full member experience.
Join For FreeI have been playing around with Scalaz
for a couple of weekends now. Scalaz brings to Scala some generic
functions and abstractions that are not there in the current Scala API.
These are mostly *functional* higher order structures that Haskell
boasts as being part of their standard distribution. Using Scalaz you
can write Scala code in applicative style that often come out as being
more expressive than the imperative one.
Functors, applicatives, arrows, monads and many such abstractions form
part of the Scalaz repertoire. It’s no wonder that using Scalaz needs a
new way of thinking on your part than is with standard Scala. You need
to think more like as if you’re modeling in Haskell rather than in any
object-oriented language.
Typeclasses are the cornerstone of Scalaz distribution. Instead of
thinking polymorphically in inheritance hierarchies, think in terms of
designing APIs for the open world
using typeclasses. Scalaz implements the Haskell hierarchy of
typeclasses - Functors, Pointed, Applicative, Monad and the associated
operations that come with them.
How is this different from the normal way of thinking ? Let’s consider an example from the current Scala point of view.
We say that with Scala we can design monadic abstractions. flatMap is the bind which helps us glue abstractions just like you would do with >>=
of Haskell. But does the Scala standard library really have a monad
abstraction ? No! If it had a monad then we would have been able to
abstract over it as a separate type and implement APIs like sequence in Haskell ..
sequence :: Monad m => [m a] -> m [a]
We don’t have this in the Scala standard library. Consider another example of a typeclass, Applicative, which is defined in Haskell as
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
…
Here pure lifts a into the effectful environment of the functor, while (<*>) takes a function from within the functor and applies it over the values of the functor. Scalaz implements Applicative in Scala, so that you can write the following:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> List(10, 20, 30) <*> (List(1, 2, 3) map ((_: Int) * (_: Int)).curried)
res14: List[Int] = List(10, 20, 30, 20, 40, 60, 30, 60, 90)
Here we have a pure function that multiplies 2 Ints. We curry the function and partially apply to the members of the List(1, 2, 3). Note List is an instance of Applicative Functor. Then we get a List of partial applications. Finally <*> takes that List and applies to every member of List(10, 20, 30) as a cartesian product. Of course the Haskell variant is much less verbose ..
(*) <$> [1, 2, 3] <*> [10, 20, 30]
and this is due to better type inference and curry by default strategy of function application.
You can get a more succinct variant in Scalaz using the |@| combinator ..
scala> List(10, 20, 30) |@| List(1, 2, 3) apply (_ * _)
res17: List[Int] = List(10, 20, 30, 20, 40, 60, 30, 60, 90)
You can have many instances of Applicatives so long you implement the
contract that the above definition mandates. Typeclasses give you the
option to define abstractions for the open world. Like List,
there are many other applicatives implemented in Scalaz like options,
tuples, function applications etc. The beauty of this implementation is
that you can abstract over them in a uniform way through the power of
the Scala type system. Just like List, you can apply <*> over options as well ..
scala> some(10) <*> (some(20) map ((_: Int) * (_: Int)).curried)
res18: Option[Int] = Some(200)
And since all Applicatives can be abstracted over without looking at the
exact concrete type, here’s one that mixes an option with a function
application through <*> ..
scala> some(9) <*> some((_: Int) + 3)
res19: Option[Int] = Some(12)
The Haskell equivalent of this one is ..
Just (+3) <*> Just 9
Scalaz uses two features of Scala to the fullest extent - higher kinds
and implicits. The entire design of Scalaz is quite unlike the usual
Scala based design that you would encounter elsewhere. Sometimes you
will find these implementations quite opaque and verbose. But most of
the verbosity comes from the way we encode typeclasses in Scala using
implicits. Consider the following definition of map, which is available
as a pimp defined in the trait MA ..
sealed trait MA[M[_], A] extends PimpedType[M[A]] {
import Scalaz._
//..
def map[B](f: A => B)(implicit t: Functor[M]): M[B] = //..
//..
}
map takes a pure function (f: A => B) and can be applied on any type constructor M so long it gets an instance of a Functor[M] in its implicit context. Using the trait we pimp the specific type constructor with the map function.
Here are some examples of using applicatives and functors in Scalaz. For fun I had translated a few examples from Learn You a Haskell for Great Good. I also mention the corresponding Haskell version for each of them ..
// pure (+3) <*> Just 10 << from lyah
10.pure[Option] <*> some((_: Int) + 3) should equal(Some(13))
// pure (+) <*> Just 3 <*> Just 5 << lyah
// Note how pure lifts the function into Option applicative
// scala> p2c.pure[Option]
// res6: Option[(Int) => (Int) => Int] = Some(<function1>)
// scala> p2c
// res7: (Int) => (Int) => Int = <function1>
val p2c = ((_: Int) * (_: Int)).curried
some(5) <*> (some(3) <*> p2c.pure[Option]) should equal(Some(15))
// none if any one is none
some(9) <*> none should equal(none)
// (++) <$> Just "johntra" <*> Just "volta" << lyah
some("volta") <*> (some("johntra") map (((_: String) ++ (_: String)).curried))
should equal(Some("johntravolta"))
// more succinct
some("johntra") |@| (some("volta") apply (_ ++ _) should equal(Some("johntravolta"))
Scalaz is mind bending. It makes you think differently. In this post I
have only scratched the surface and talked about a couple of
typeclasses. But the only way to learn Scalaz is to run through the
codebase. It's dense but if you like functional programming you will get
lots of aha! moments going through it.
In the next post I will discuss how I translated part of a domain model of a financial trade system which I wrote in Haskell (Part 1, Part 2 and Part 3) into Scalaz. It has been a fun exercise for me and shows how you can write your Scala code in an applicative style.
From http://debasishg.blogspot.com/2010/11/exploring-scalaz.html
Opinions expressed by DZone contributors are their own.
Comments