Applicative vs Monad In Scala


applicative functor, functional programming, monad, scala

Applicative vs Monad In Scala


Daniel Shin - October 18, 2015

In previous article, we’ve looked at how Monad is a more capable subclass of Applicative like being able to depend on previous monadic operation, etc.

So does that mean we should always opt for Monad?

No. In fact, Applicative is more preferable to Monad when all you need is to execute similar operations together.

Monad is more liberal form of Applicative in that it adds one more trait; to chain / depend on previous operation. However, being less restricted means that the typeclass can make less assumptions about its instance.

For example, by being more restricted (as in, the operations must be independent of each other) in Applicative, it makes possible the parallel execution of the operations. Whereas in Monad, since its restriction on the traits of instances is more liberal (in that, the operations may depend on previous operations), the typeclass cannot make guarantees about whether it’s safe to execute them in parallel. Therefore, it opts for sequential execution for all monadic operations.

This becomes clear if you look at the signature of ap (the primitive method to form Applicative) in Monad from scalaz:

override def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B] = {
  bind(f)(map(fa))
}

In scalaz, bind is synonymous to flatMap.

As you can see, overriden ap method is nothing more than a combination of bind and map and since bind method must guarantee the dependable operations and therefore must be sequential execution, by extension, ap method is also a sequential execution.

To put it in different perspective, Applicative requires the structure of the entire computations to be defined before the execution happens, whereas in Monad, the structure of the entire computations is purely dependent upon the individual computation itself (it’s ad-hoc).

Look at the example:

(Future(1) |@| Future(2) |@| Future(3)) { _ + _ + _ } // Future(6)
// vs
Future(1).flatMap(one => Future(one + 2).flatMap(three => Future(three + 3))) // Future(6)

Both operations (the first, being applicative operation and the second, being monadic operation) produce the same result. But as you can see, in second operation, we have a choice of deciding what operation to run, possibly depending on the result of the previous operation. This isn’t the case with Applicative where the entire operation’s structure is defined even before the execution happens. What it does is simple. If all Future operations returns success value, then the last block will be run if not, Future with failed result is returned instead.

The main point here is that all these Future operations in Applicative will be all run in parallel regardless of the success / failure of the previous operations and will only be accumulated or folded when all operations are finished. If all finished successfully, then the block runs otherwise, default operation defined by ap method runs - usually returning failure.

In Applicative, each operation associates only at the end. This has another connotation with regards to being able to accumulate failures since all operations will all be run first independent of other operations. Although, strictly speaking, this can also be implemented in Monad (since it is a superset of Applicative), it is more natural to do with Applicative in terms of semantics of being able to run all operations in parallel.

Takeaway

Use Applicative if all you need is to run a batch of independent operations together and fold them at the end with added benefit of being able to run in parallel.

Use Monad if you need to run operations that are possibly dependent on previous operations with (obvious) downside of running in sequential order.

Caveat

Parallel execution in the context Applicative is mere theoretical possibility. The actual implementation of whether it’s actually parallel or not is completely dependent on the judgement of the writer.