Wednesday 27 June 2012

Scala: Applicative with scalaz example


I’ve talked on Functor previously, will repeat myself that construction is very useful in the Scala's world. We covered Comonad as well, it is extending Functor and adds functionality for the extraction value or the wrapping of already wrapped.
This post is related to Applicative. Applicative is coupled with Functor, even has an alternative name: Applicative Functor. At first let look into class hierarchy for Applicative in scalaz library:

Inside


Starting from the simplest:
Pure: class that knows how to wrap the value: pure method accepts any type and returns P[_]. Is reasonable – if there is a way how explicitly unwrap the value (Copure) – there should be something to wrap. Pointed just mixing Pure and Functor together.
Apply: it’s better to start from refreshing what Functor does. Functor accepts v: F[A] and function: A => B as a parameters. It applies function to v and returns F[B]. Apply declares apply method that accepts Z[A] and Z[A => B]. Application of A => B to Z[A] returns Z[B]. The difference to Functor is simple: function is wrapped as well.

When

Problem: every construction should solve for the name os something, otherwise it will be forgotten;) For Applicative business is simple: Given a function that takes multiple arguments A,B,… that returns a Result, how can that function by applied to arguments M[A], M[B],… and returns M[Result]? M - can be Monad, Functor, Comand, Applicative etc functional nature wrapper. Of corse scalaz library implements this natures as ad-hoc polymorphism.
I'm going to be closer to the example: imagine situation: system reads 3 properties from files and uses the sum of 3 somewhere. Let's talk in Scala.

No sugar way

1. "Something" reads properties and assigne them to vals.
2. and using for construction to put them together: It works well - if one from the Options is None - result is None, there is no magic, just a language construction. 3. Let's try to play via Functor map method. While map for 2 arguments, we are going even to simplify the task, add integer to 1-st Option: Looks like the only one way we can proceed with map is having Option[Int => Int]. Kinda new construction for us and we might need to have an another one 'map' method that accepts M[A => B]. 4. We are not inventing the new approaches, just studying new for us ;) Here is a source code of Applicative: we already know the fmap method from Functor, and we know that apply comes from the Apply, it accepts something that we've already seen in item 3: (f: Z[A => B], a: Z[A]) and returns Z[B]. 5. Progressing with a dirty solution: This code is important to understand, I'm going to describe it in 'colours':
a) add: (Int, Int) => Int is just a method declaration, that is implementing '+' synthetic function.

b) addC - we are converting add function to curried version, it will look like: c) map method is quite long, but simple:
implicitly[Functor[Option]] - injects implicit Functor for an Option, Functor of course is
coming from the scalaz library. Actually map is just a call curried function with internal value, and as the result we are getting partially applied function Int => Int.
It can be presented like this: d) There are:

- partially applied function: inside M[A => B]. Some[Int=>Int] = Some [{v0.get + _}]
- M[A] = M[Int]

This is exactly what apply method is expecting as parameters.

e) implicitly[Apply[Option]] - is "injected" from scalaz - surprise). Implementation is using Option's "native" map and flatMap methods. Here are detailed steps: f) Finally, for the third param we are simple using: g) scalaz's way:
h) And better style:

Sugar style

scalaz offers better syntax:
There is a good case example with classes, when some input (from command line or web page) should be converted into the some class instance, of course while human is involved (inputs) the result should be Option, here is a cool solution based on Applicative: of course result can be none if any param is missing: additionally we can use a sugar for default value:

At the end:

I wasn't touching algebras during the presentation. Even for such complex type as Applicative. Usually function programming construction are described via the mess of greek symbols. I support this way is mandatory to be an subject expert, but it's not the best to start on. For future I recommend to read something on Category Theory or at least do a review for the formulas from where Applicative, Monad etc are coming to. Even scala-doc for scalaz operates with Monadic laws and other important to know facts. As a quick drop to Applicative theory: All instances of Applicative must satisfy 4 laws:
  1. identity
    forall a. a == apply(a, pure(identity))
  2. composition
    forall af ag a. apply(apply(a, ag), af) == apply(a, apply(ag, apply(af, pure(compose))))
  3. homomorphism
    forall f a. apply(pure(a), pure(f)) == pure(f(a))
  4. interchange
    forall af a. apply(pure(a), af) == apply(af, pure(f => f(x)))

And from here comes that fmap can be presented via apply and pure: Don' stop)

No comments:

Post a Comment