I’ve been using Scala at work and to understand Functional Programming more deeply I picked Graham Hutton’s Programming in Haskell (love it :)
In the chapter on Monads I got my first look into the concept of Applicative Functors (AFs)
In my (limited) professional-Scala capacity I’ve never had to use AFs and have always written code that uses Monads. I’m trying to distill the understanding of “when to use AFs” and hence the question. Is this insight correct:
If all your computations are independent and parallelizable (i.e., the result of one doesn’t determine the output of another) your needs would be better served by an AF if the output needs to be piped to a pure function without effects. If however, you have even a single dependency AFs won’t help and you’ll be forced to use Monads. If the output needs to be piped to a function with effects (e.g., returning Maybe) you’ll need Monads.
For example, if you have “monadic” code like so:
val result = for {
x <- callServiceX(...)
y <- callServiceY(...) //not dependent on X
} yield f(x,y)
It’s better to do something like (pseudo-AF syntax for scala where |@|
is like a separator between parallel/asynch calls).
val result = (callServiceX(...) |@| callServiceY(...)).f(_,_)
f == pure and callService* are independent
AFs will serve you betterf
has effects i.e., f(x,y): Option[Response]
you’ll need MonadscallServiceX(...), y <- callServiceY(...), callServiceZ(y)
i.e., there is even a single dependency in the chain, use Monads.Is my understanding correct? I know there’s a lot more to AFs/Monads and I believe I understand the advantages of one over the other (for the most part). What I want to know is the decision making process of deciding which one to use in a particular context.
Every Monad is an Applicative Just as IO , every monad can be made into an applicative functor.
Generalizing MonadsA monad can be seen as a combination of a functor (we have M[A] with a map that permits us to apply f to each element in M[A] and obtain M[M[B]]) and a monoid (that permits to flatten M[M[B]] into M[B] by means of the associative operator: e.g., concatenation in the case of lists).
A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.
A functor takes a pure function (and a functorial value) whereas a monad takes a Kleisli arrow, i.e. a function that returns a monad (and a monadic value). Hence you can chain two monads and the second monad can depend on the result of the previous one. You cannot do this with functors.
There is not really a decision to be made here: always use the Applicative interface, unless it is too weak.1
It's the essential tension of abstraction strength: more computations can be expressed with Monad; computations expressed with Applicative can be used in more ways.
You seem to be mostly correct about the conditions where you need to use Monad. I'm not sure about this one:
- If
f
has effects i.e.f(x,y) : Option[Response]
you'll need Monads.
Not necessarily. What is the functor in question here? There is nothing stopping you from creating a F[Option[X]]
if F
is the applicative. But just as before you won't be able to make further decisions in F
depending on whether the Option
succeeded or not -- the whole "call tree" of F
actions must be knowable without computing any values.
1Readability concerns aside, that is. Monadic code will probably be more approachable to people from traditional backgrounds because of its imperative look.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With