class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
From my understanding, it takes a function f, where another function (a -> b)
as its argument, returns a function f
. Applying f
to a
then returns a function f
and apply f
to b
.
Here is an example:
Prelude> (+) <$> Just 2 <*> Just 3
Just 5
But I don't quite understand how it works.
I guess (+)
should be f
, Just 2
and Just 3
should be a
and b
respectively. Then what is (a -> b)
?
From my understanding, it takes a function f...
Unfortunately this is incorrect. In this case, f
is a type, not a function. Specifically, f
is a "higher-kinded type" with kind * -> *
. The type f
is the functor.
In this case, f
is Maybe
. So we can rewrite the function types, specializing them for Maybe
:
pure :: a -> Maybe a
(<*>) :: Maybe (a -> b) -> Maybe a -> Maybe b
It starts to become a bit clearer once you get this far. There are a couple different possible definitions for pure
, but only one that makes sense:
pure = Just
The operator x <$> y
is the same as pure x <*> y
, so if you write out:
(+) <$> Just 2 <*> Just 3
Then we can rewrite it as:
pure (+) <*> pure 2 <*> pure 3
Although this technically has a more general type. Working with the functor laws, we know that pure x <*> pure y
is the same as pure (x y)
, so we get
pure ((+) 2) <*> pure 3
pure ((+) 2 3)
pure (2 + 3)
In this case, we a
and b
are the types but since <*>
appears twice they actually have different types in each case.
In the first <*>
, a
is Int
and b
is Int -> Int
.
In the second <*>
, both a
and b
are Int
. (Technically you get generic versions of Int
but that's not really important to the question.)