Monday, 18 June 2012

Scala: Functor (scalaz example)

Function programming got a proven handy in structuring the code, from where we have common structures/classes presented in many functional languages. All this magic comes from mathematic and in the most parts aren't so easy to get for regular developers with imperative experience. I've been googling a lot to get essence and still getting a lot of "news". Looks like the best way to review FP types in Scala is using examples from scalaz library. Let try to start from the Functor.

Functor 

Functor is a something you can map over, it has only one method map (called fmap scalaz). Class diagram is very simple:
And Scala view is exactly the same.
trait InvariantFunctor[F[_]] {
def xmap[A, B](ma: F[A], f: A => B, g: B => A): F[B]
}
trait Functor[F[_]] extends InvariantFunctor[F] {
def fmap[A, B](r: F[A], f: A => B): F[B]
final def xmap[A, B](ma: F[A], f: A => B, g: B => A) = fmap(ma, f)
}
view raw functor.scala hosted with ❤ by GitHub
InvariantFunctor defines method what accepts Invariant transformation from A to B and from B back to A (without losing something). Covariant implementation of this function in Functor just ignores second param. Lets implement example:
implicit def OptionFunctor: Functor[Option] = new Functor[Option] {
def fmap[A, B](r: Option[A], f: A => B) = r match {
case Some(a) => Some(f(a))
case _ => None
}
}
keeping in mind that Scala's Option is Functor as well implementation can look like delegation:
implicit def OptionFunctor: Functor[Option] = new Functor[Option] {
def fmap[A, B](r: Option[A], f: A => B) = r map f
}
Second variant is coming from scalaz library. If we look inside Functor.scala from this library we will see a lot of implicit declarations for Functor implementation. They are all used as Type Classes for a specific functor. Lets imagine situation when we want to declare map method that accepts all types:
def map[F[_], A, B](r: F[A], f: A => B)(implicit fn: Functor[F]) = fn fmap(r, f)
view raw map.scala hosted with ❤ by GitHub
By the way it looks as context bound declaration, lets refactoring it according previous lesson:
def map[F[_] : Functor, A, B](r: F[A], f: A => B) = implicitly[Functor[F]] fmap(r, f)
view raw map2.scala hosted with ❤ by GitHub
and of course an example with Option type:
println(map(Option(3), (i: Int) => i.toString)) // Some(3)
view raw optionMap.scala hosted with ❤ by GitHub
Here we got a working Functor, coming as Context Bounded construction. There are heavier examples in scalaz library:
import scalaz._
import Scalaz._
println(List(1, 2, 3, 4, 5) ∘ (1 +)) // List(2, 3, 4, 5, 6)
view raw example1.scala hosted with ❤ by GitHub
Looks like the mess, almost Clojure look&feel;) Don'w worry, we are going to review in the details:
1. List is implicitly converted to MASugar type, that looks like:
trait MASugar[M[_], A] {
self: MA[M, A] =>
/** Alias for map */
def ∘[B](f: A => B)(implicit t: Functor[M]): M[B] = map(f)
...
}
view raw MASugar.scala hosted with ❤ by GitHub
2. Method ∘ is calling for a map method that is wrapper around fmap in functor (implicit parameter):
def map[B](f: A => B)(implicit t: Functor[M]): M[B] = t.fmap(value, f)
view raw map3.scala hosted with ❤ by GitHub
, as we can see it is almost equals to our early example. Actually looking into Functor.scala file we can guess that TraversableFunctor will be injected in our case. It is delegates fmap call to map on List. Ha, quite useless construction for List, while List as Functor as well as an Option! As we will find later in Standart Scala library all Monadic classes are Functor classes in the same time! 3. Construction (1 +) is implicitly converted to function1, suppose it's not so clean, here is an example of usage Synthetic Function:
val plusOne:Int => Int = (1 +)
println(plusOne) //<function1>
println((10 -):Int => Int) //<function1>
view raw function1.scala hosted with ❤ by GitHub
Explicit type declaration is required, otherwise compiler will assume that this is not finished expression. And if you want you can review complex Functor example with sclaz library, Map across the Option functor within a map across the List functor:

    (List(Some(7), None, Some(8)) ∘∘ (1 + (_: Int))) // List(Some(8), None, Some(9))

Cheers!

No comments:

Post a Comment