6

I am having some trouble understanding how the function instance (->) r of Applicative works in Haskell.

For example if I have

(+) <$> (+3) <*> (*100) $ 5

I know you get the result 508, I sort of understand that you take the result of (5 + 3) and (5 * 100) and you apply the (+) function to both of these.

However I do not quite understand what is going on. I assume that the expression is parenthesized as follows:

((+) <$> (+3)) <*> (*100)

From my understanding what is happening is that your mapping (+) over the eventual result of (+3) and then you are using the <*> operator to apply that function to the eventual result of (*100)

However I do not understand the implementation of <*> for the (->) r instance and why I cannot write:

(+3) <*> (*100)

How does the <*>, <$> operator work when it comes to (->) r?

Micha Wiedenmann
  • 19,979
  • 21
  • 92
  • 137
Yusuf
  • 491
  • 2
  • 8
  • 1
    I believe you are missing a `<$>`: `(+) <$> (+3) <*> (*100) $ 5`. It parses as `(((+) <$> (+3)) <*> (*100)) $ 5`. – Daniel Wagner Aug 04 '16 at 22:25
  • @DanielWagner The `<$>`s got eaten by the markdown parser (but the `<*>`s didn’t, for whatever reason). I’ve added the necessary code escapes. – Alexis King Aug 04 '16 at 22:28
  • Ah i see thank you, i didn't know you had to escape the `<$>` – Yusuf Aug 04 '16 at 22:30

5 Answers5

9

<$> is just another name for fmap and its definition for (->) r is (.) (the composition operator):

intance Functor ((->) r) where
  fmap f g = f . g

You can basically work out the implementation for <*> just by looking at the types:

instance Applicative ((->) r) where
  (<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
  f <*> g = \x -> f x (g x)

You have a function from r to a to b and a function from r to a. You want a funtion from r to b as a result. First thing you know is you return a function:

\x ->

Now you want to apply f since it is the only item which may return a b:

\x -> f _ _

Now the arguments for f are of type r and a. r is simply x (since it alrady is of type r and you can get an a by applying g to x:

\x -> f x (g x)

Aaand you're done. Here's a link to the implementation in Haskell's Prelude.

ThreeFx
  • 7,250
  • 1
  • 27
  • 51
6

Consider the type signature of <*>:

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

Compare this to the type signature for ordinary function application, $:

($) :: (a -> b) -> a -> b

Notice that they are extremely similar! Indeed, the <*> operator effectively generalizes application so that it can be overloaded based on the types involved. This is easy to see when using the simplest Applicative, Identity:

ghci> Identity (+) <*> Identity 1 <*> Identity 2
Identity 3

This can also be seen with slightly more complicated applicative functors, such as Maybe:

ghci> Just (+) <*> Just 1 <*> Just 2
Just 3
ghci> Just (+) <*> Nothing <*> Just 2
Nothing

For (->) r, the Applicative instance performs a sort of function composition, which produces a new function that accepts a sort of “context” and threads it to all of the values to produce the function and its arguments:

ghci> ((\_ -> (+)) <*> (+ 3) <*> (* 100)) 5
508

In the above example, I have only used <*>, so I’ve explicitly written out the first argument as ignoring its argument and always producing (+). However, Applicative typeclass also includes the pure function, which has the same purpose of “lifting” a pure value into an applicative functor:

ghci> (pure (+) <*> (+ 3) <*> (* 100)) 5
508

In practice, though, you will rarely see pure x <*> y because it is precisely equivalent to x <$> y by the Applicative laws, since <$> is just an infix synonym for fmap. Therefore, we have the common idiom:

ghci> ((+) <$> (+ 3) <*> (* 100)) 5
508

More generally, if you see any expression that looks like this:

f <$> a <*> b

…you can read it more or less like the ordinary function application f a b, except in the context of a particular Applicative instance’s idioms. In fact, an original formulation of Applicative proposed the idea of “idiom brackets”, which would add the following as syntactic sugar for the above expression:

(| f a b |)

However, Haskellers seem to be satisfied enough with the infix operators that the benefits of adding the additional syntax has not been deemed worth the cost, so <$> and <*> remain necessary.

Alexis King
  • 43,109
  • 15
  • 131
  • 205
4

Let's take a look at the types of these functions (and the definitions that we automatically get along with them):

(<$>) :: (a -> b) -> (r -> a) -> r -> b
f <$> g = \x -> f (g x)

(<*>) :: (r -> a -> b) -> (r -> a) -> r -> b
f <*> g = \x -> f x (g x)

In the first case, <$>, is really just function composition. A simpler definition would be (<$>) = (.).

The second case is a little more confusing. Our first input is a function f :: r -> a -> b, and we need to get an output of type b. We can provide x :: r as the first argument to f, but the only way we can get something of type a for the second argument is by applying g :: r -> a to x :: r.


As an interesting aside, <*> is really the S function from SKI combinatory calculus, whereas pure for (-> r) is the K :: a -> b -> a (constant) function.

eigenchris
  • 5,791
  • 2
  • 21
  • 30
4

As a Haskell newbie myself, i'll try to explain the best way i can

The <$> operator is the same as mapping a function on to another function.

When you do this:

(+) <$> (+3)

You are basically doing this:

fmap (+) (+3)

The above will call the Functor implementation of (->) r which is the following:

fmap f g = (\x -> f (g x))

So the result of fmap (+) (+3) is (\x -> (+) (x + 3))

Note that the result of this expression has a type of a -> (a -> a)

Which is an applicative! That is why you can pass the result of (+) <$> (+3) to the <*> operator!

Why is it an applicative you might ask? Lets look the at the <*> definition:

f (a -> b) -> f a -> f b 

Notice that the first argument matches our returned function definition a -> (a -> a)

Now if we look at the <*> operator implementation, it looks like this:

f <*> g = (\x -> f x (g x))

So when we put all those pieces together, we get this:

(+) <$> (+3) <*> (+5)
(\x -> (+) (x + 3)) <*> (+5)
(\y -> (\x -> (+) (x + 3)) y (y + 5))
(\y -> (+) (y + 3) (y + 5))
Fábio Junqueira
  • 2,701
  • 21
  • 20
3

The (->) e Functor and Applicative instances tend to be a bit confusing. It may help to view (->) e as an "undressed" version of Reader e.

newtype Reader e a = Reader
  { runReader :: e -> a }

The name e is supposed to suggest the word "environment". The type Reader e a should be read as "a computation that produces a value of type a given an environment of type e".

Given a computation of type Reader e a, you can modify its output:

instance Functor (Reader e) where
  fmap f r = Reader $ \e -> f (runReader r e)

That is, first run the computation in the given environment, then apply the mapping function.

instance Applicative (Reader e) where
  -- Produce a value without using the environment
  pure a = Reader $ \ _e -> a

  -- Produce a function and a value using the same environment;
  -- apply the function to the value

  rf <*> rx = Reader $ \e -> (runReader rf e) (runReader rx e)

You can use the usual Applicative reasoning for this as any other applicative functor.

dfeuer
  • 48,079
  • 5
  • 63
  • 167