In the previous blog post I claimed that Functors, Monads and Applicatives can be presented in an easily comprehensible way:
a) ( A=>B ) => ( C[A]=>C[B] ) | Functor
b) ( A=>C[B] ) => ( C[A]=>C[B] ) | Monad
c) ( C[A=>B] ) => ( C[A]=>C[B] ) | Applicative
Thus they are perceived as transformations/conversions from some function type involving an A and a B to the type C[A]=>C[B] .
We then created a container MyBox, which can keep a value of some type, and some functions which implemented the above mentioned signatures.
For example the Functor transformation was implemented as a standalone function named map :
def map[A,B]( rawfunc:A=>B ) : MyBox[A]=>MyBox[B] = (a:MyBox[A]) => new MyBox( rawfunc(a.value) )
But what I want you to understand in this post, is that this –map as a function taking a function- is only one possible implementation, and that there are very different ways to structure the concept represented by the function map . This is much more true for Scala, which is a feature rich hybrid OO-FP language. To demonstrate this, let’s stay with Functors and the shown map function. What you see here will be applicable for the other concepts, Monad and Applicative, too.
First, let us again have a look at the signature of map:
( A=>B ) => ( MyBox[A]=>MyBox[B] )
According to this signature, map takes a function of type A=>B and returns a function which takes a MyBox[A]. In the end the result is of type MyBox[B]. The second pair of parens can easily be left out:
( A=>B ) => MyBox[A] => MyBox[B]
What we now see is a curried form that we can transform back into a multi-parameter function:
((A=>B), MyBox[A]) => MyBox[B]
We now have a function taking two parameters -a function A=>B and a MyBox[A]- and resulting in MyBox[B]. In such a case the order of the parameters isn’t really important, so we can change that easily to
( MyBox[A],(A=>B)) => MyBox[B]
what in curried form again is
MyBox[A] => (A=>B) => MyBox[B]
Let’s take our first implementation and adapt that according to this signature:
def map[A,B](a:MyBox[A]): (A=>B)=>MyBox[B] = (rawfunc:A=>B) => new MyBox( rawfunc(a.value) )
Now map first takes the container to work on and then returns a function that is bound to that container, but expects as parameter the bare typed function which shall be applied to produce the result.
This is a signature for map that you will also find in articles, either as a primary way to implement it, or as a helper function, delegating to the other signature (like here).
So far the class MyBox and the function map are only loosely related, i.e. the function map can be located anywhere in our code base. To organize it better and document the relationship of map with MyBox, we could possibly declare it as a method on a companion object:
object MyBox { def map[A,B](a:MyBox[A]): (A=>B)=>MyBox[B] = (rawfunc:A=>B) => new MyBox( rawfunc(a.value) ) }
Much more, if we have a function that takes an instance of some class as its first parameter, this is only a functional way to describe a method, which in classical OO would belong to the class itself. So the signature
(MyBox[A],(A=>B)) => MyBox[B]
is a hint, that maybe map should be implemented like this:
class MyBox[A](val value:A) { def map[A,B](rawfunc:A=>B) = new MyBox( rawfunc(this.value) ) }
So now we can call map directly on an instance of MyBox:
val n = new MyBox("hello") n.map( (a:String)=>a.length ).value // -> 5
This is a nice way to implement Functors and Monads in case you are the class’ author and have such features already in mind. As you may know, Scala’s for-comprehension is only syntactic sugar for calls to map and flatMap on instances. Therefore our class can be used inside such expressions too.
Another Scala-typical way to provide methods on objects although their class does not implement them are wrapper classes and implicit conversions. How this would look like for our MyBox, you see below, including also the Monad method flatMap.
class MyBox[T](val value:T) { override def toString() = "MyBox("+value+")" } class MyBoxWrapper[T](w:MyBox[T]) { def flatMap[R](f:T=>MyBox[R]) = map(f).value def map[R](f:T=>R) = new MyBox(f(w.value)) } object TestMonadWrapper { implicit def myBoxToWrapper[S](mb:MyBox[S]) = new MyBoxWrapper[S](mb) def main(args:Array[String]) { val ma = new MyBox("hello world") println ( ma.map((a) => a.length) ) val res = for { a <- ma } yield a.length + 2 println(res) val mb = new MyBox("hola mundo") val res2 = for { a <- ma b <- mb } yield a.size + b.size println (res2) } }
This is an extended example, so let’s have a walk-through. Class MyBox implements a toString Method to allow for convenient printing on console, so we don’t have to call .value to see what the value is. MyBox does not implement the methods map and flatMap itself, but we have a wrapper class MyBoxWrapper that implements them for us. Our application object TestMonadWrapper contains some tests in the main method, and an implicit conversion that wraps a MyBox instance into a MyBoxWrapper whereever the methods map and flatMap are called on a MyBox instance. This is the case for ma and mb inside the for-comprehensions.
Last but not least Scala also knows typeclasses -not as a language feature but as a pattern-, which are just another way to provide function implementations for specific types, a concept taken from Haskell. I don’t want to dive into that matter here now. To see Monads implemented as a typeclass, have a look into Daniel Spiewak’s article Monads are not metaphors , where the concept Monad is shown as a trait, implemented by two implicit objects for different types, or at Eric Torreborre’s article The Essence of the Iterator Pattern, where he demonstrates Functor and Applicative as typeclasses right from the beginning.
In my examples you have so far always seen the functions named map, flatMap and apply. The first two match with how the Scala standard library is implemented and the for-comprehension works. But the name apply must be taken with care, as this is in Scala a conventional method to use object references like functions.
Indeed, other languages implement the concepts of Functor, Monad and Applicative too but use other names for the functions. Namely in Haskell, the source of those ideas, the methods are named differently and sometimes confusingly for a Scala developer. So is the method map named fmap there, not to be confused with Scala’s flatMap, which is named bind in Haskell. Beside that, Haskell uses even symbolic names for the functions.
Another blog post which I found recently and which presents Functors, Monads and Applicatives in a similar reduced way like my first post, is Functors, Applicative Functors, and Monads aren’t that scary. The author took the Haskell function names for Scala too.
All in all, the names are of no importance. Much more, have a look at the signatures to recognise the patterns shown in the first post, even when they are mutated, like seen in this post.
In the next post we will play with map, discover the other two function signatures of the first post, and think about how it comes that we would need flatMap and apply.
I like your post very much, it’s very instructive. Many Thanks.
Comment by simon — May 16, 2012 @ 2:11 pm
[…] https://thedet.wordpress.com/2012/05/04/functors-monads-applicatives-different-implementations/ […]
Pingback by Monads functors and applicatives http thedet wordpress com… « tylerweir — May 17, 2012 @ 6:10 pm
[…] the last post in this series I used the map function as a demonstration for different styles to implement the three concepts. […]
Pingback by Functors, Monads, Applicatives – playing with map (Functor) « The Det about Programming — May 20, 2012 @ 5:37 pm
[…] also had a different implementation in the second article (I change the type variables here, and method to function, to harmonise with the code […]
Pingback by Functors, Monads, Applicatives – taking Monad apart [DRAFT] « de.velopmind | The Det about Programming — January 21, 2013 @ 1:22 am
[…] post is about Functors, or more precisely the map method, and is especially related to my post: Functors, Monads, Applicatives – different implementations . It is therefore recommended that you (re)read that one, and best its predecessor, before. (I […]
Pingback by Functors in images « de.velopmind | The Det about Programming — February 11, 2013 @ 2:40 pm