Search code examples
haskellfunctorapplicative

what does (<*>) :: f (a -> b) -> f a -> f b exactly do in the Functor class


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)?


Solution

  • 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.)