Question

I've recently read an article about Functors, Applicatives and Monads in Haskell and it concludes with these statements:

  • functors: you apply a function to a wrapped value using fmap or <$>
  • applicatives: you apply a wrapped function to a wrapped value using <*> or liftA
  • monads: you apply a function that returns a wrapped value, to a wrapped value using >>= or liftM

This is all clear but I don't get what the purpose of the a wrapped value is. Do they work like the decorators in python or the Optional in java? I would greatly appreciate some practical example from the real world.

Was it helpful?

Solution

Java's Optional is essentially equivalent to Haskell's Maybe monad, so that's a good starting place for understanding how these "wrapped values" behave.

Functors, Applicatives and Monads are ways of modelling some kind of additional behavior that can modify an existing type. Maybe takes a type, and modifies it so that it can model both a one single instance or no instance of that type. List modifies a type so that it models a sequence of instances of the given type.

Each of these typeclasses provides a way to apply a given function to the wrapped type, respecting the modifications that the wrapping type makes. For example, in Haskell,

Functors

Functors use the fmap or <$> functions (different names for the same thing):

  • fmap or <$> Functor f => (a -> b) -> f a -> f b

This takes a function and applies to to the wrapped elements

fmap (\x -> x + 1) (Just 1)   -- Applies (+1) to the inner value, returning (Just 2)
fmap (\x -> x + 1) Nothing    -- Applies (+1) to an empty wrapper, returning Nothing

fmap (\x -> x + 1) [1, 2, 3]  -- Applies (+1) to all inner values, returning [2, 3, 4]
(\x -> x + 1) <$> [1, 2, 3]   -- Same as above

Applicatives

Applicatives use the <*> function:

  • <*> Applicative f => f (a -> b) -> f a -> f b

This takes a wrapped function and applies it to the wrapped elements

(Just (\x -> x + 1)) <*> (Just 1) -- Returns (Just 2)
(Just (\x -> x + 1)) <*> Nothing  -- Returns Nothing
Nothing <*> (Just 1)              -- Returns Nothing
[(*2), (*4)] <*> [1, 2]           -- Returns [2, 4, 4, 8]

Monads

There are two relevant functions in the Monad typeclass:

  • return Monad m => a -> m a
  • (>>=) Monad m => m a -> (a -> m b) -> m b (pronounced "bind")

The return function bears no resemblance to the one you may be familiar with in C-style languages. It takes a raw, unwrapped value, and wraps it up in the desired monadic type.

makeJust :: a -> Maybe a
makeJust x = return x

let foo = makeJust 10 -- returns (Just 10)

The bind function lets you temporarily unwrap the inner elements of a Monad and pass them to a function that performs some action that wraps them back UP in the same monad. This can be used with the return function in trivial cases:

[1, 2, 3, 4] >>= (\x -> return (x + 1))  -- Returns [2, 3, 4, 5]
(Just 1) >>= (\x -> return (x + 1))      -- Returns (Just 2)
Nothing >>= (\x -> return (x + 1))       -- Returns Nothing

Where it gets interesting is when you have functions to chain together that don't require you to use return. I like to use getLine and putStrLn as examples, which have the following signatures using the IO monad:

  • getLine IO String
  • putStrLn String -> IO ()

You can call these functions like so:

getLine >>= (\x -> putStrLn x) -- Gets a line from IO and prints it to the console
getLine >>= putStrLn -- With currying, this is the same as above

You could also use the >>= function to chain a few operations together.

-- Reads a line from IO, converts to a number, adds 10 and prints it
getLine >>= (return . read) >>= (return . (+10)) >>= putStrLn . show

OTHER TIPS

That article has the virtue of being very visual and intuitive... but at the cost of building intuitions that are often false. The concepts of functor, applicative and monad do not require you to have "wrapped" values, strictly speaking.

One good example to consider here is the concept of promises that's been gaining traction recently (particularly in Javascript). A promise, in this sense, is an object that acts as a placeholder for the result value of an asynchronous, background computation, like fetching some data from a remote service. In addition to that, it serves as a mediator between the asynchronous computation and functions that need to operate on its anticipated result.

Promise libraries usually support a functorial/monadic API where you can "chain" a function onto a promise, which produces another promise that produces the result of applying that function to the original promise's result.

This provides a vivid example of the value of the functor/monad interface, because the whole point of promises is that they allow you to say what function should apply to the result of a background task, before that task has completed. When you map a function over a promise, the value that your function should apply to may not have been computed yet (and in fact, if there is an error somewhere it may never be computed).

More generally, you can think of functor/applicative/monad as interfaces for mediator objects that sit in between functions and arguments, and connect them indirectly according to some policy. The simplest way to use a function is just to call it with some arguments; but if you have first-class functions, you have other, indirect options—you can supply the function to a mediator object that will control if, when and how many times the function will be called, and what to do with its result. Promises are one such example:

  1. They call the functions supplied to them when the result of some background task is completed
  2. The results of those functions are then handed over to other promises that are waiting for them.

Well Optional is a monad (and therfore an applicative functor and a functor), and indeed many container types are monadic as well, and people usually have a fairly good intuition for how containery type things work as these things, as I believe you do from your question.

However lots of things that aren't normally thought of as containers can also be monads, applicative or functors e.g. parsers combinators or IO or writing to a log. An alternative way of looking at them is as computations in some context, for instance, while List is obviously a container of values you can also consider computations in the List monad as representing nondeterministic computations

What I'm leading up to here is that monads are intrinsictly more abstract than the examples you are after. they are literally:

  • a type constructor
  • a return/unit function
  • and a bind (or join and fmap) function

that satisfy the 3 monad laws, there is no further unifying principle than that (for a monad, replace with relevant functions and laws for Applicative and Functor)

Licensed under: CC-BY-SA with attribution
scroll top