Staring at ($), (<$>), (<*>) and (>>=)
Recently I’ve spent some time staring at type signatures. The goal was to develop a better intuition by absorbing their wisdom. Last week it was Monad’s
bind. This time I’ve decided to compare the following four:
Function Application or
It takes a funtion from a value of type
a to a value of type
a and returns
b. There’s only one possible way to implement
($) which is to apply the funtion to the value of type
The only difference from the previous is that
b exist in a context
f. For example, we could have an
Int in a
List context (i.e.
[Int]), which means we went from one
Int to any number of
Ints. Or we could have an
Int in a
Maybe context (i.e.
Maybe Int), in other words there could be either no
Ints or just one
Int. And so on and so forth depending on the semantics of each functor.
Again, it’s easy to see how the value of type
a must feed the function from
b to generate the output. The only difference from
($) is that depending on the semantics of the context
f, the function will be applied in a different way.
Applicative Functor’s sequential application or
In this instance, the function from
b has a context
f too. Therefore, the way the output is calculated depends on both the first and the second
f (which must be the same
This time, the way the function is applied depends only on the second
m. This is the same situation as for
(<$>). But there’s one important change: the previous functions could only transform an
a into a
b. In the case of
bind, the funtion decides not only on the
b but also on the
m, which must be the same
m for both.
Let’s see the above in action in the context of
Either which has an instance for Functor, Applicative Functor and Monad. Notice that the instances are defined for
Either e because the context they provide is around one type, not two. For example, given an
Int we can provide it an
Either String context by doing
Either String Int.
show 1 --> "1" -- ($) show $ 1 --> "1" -- (<$>) show <$> Right 1 --> Right "1" show <$> Left "string" --> Left "string" -- Either maps the function only when the value is a `Right`. -- (<*>) Right show <*> Right 1 --> Right "1" Right show <*> Left "string" --> Left "string" Left show <*> Right 1 --> Left show Left show <*> Left "string" --> Type error: the type on the left should be the same for both `Either`s. -- Either applies the function only when both values are `Right`. -- (>>=) Right 1 >>= (\x -> Right (show x)) --> Right "1" Left "string" >>= (\x -> Right (show x)) --> Left "string" Right 1 >>= (\x -> Left (show x)) --> Left "string" Left "string" >>= (\x -> Left (show x)) --> Left "string" -- Either binds the function only when the value before `>>=` is a `Right`. -- Contrarily to the previous cases, `>>=` can decide to return `Left` or `Right`.