Chapter 11 of Learn You a Haskell introduces the following definition:
instance Applicative ((->) r) where pure x = (\_ -> x) f <*> g = \x -> f x (g x) Here, the author engages in some uncharacteristic hand-waving ("The instance implementation for <*> is a bit cryptic, so it's best if we just [show it in action without explaining it]"). I'm hoping someone here might help me figure it out.
According to the applicative class definition, (<*>) :: f (a -> b) -> f a -> f b
In the instance, substituting ((->)r) for f: r->(a->b)->(r->a)->(r->b)
So the first question, is how do I get from that type to f <*> g = \x -> f x (g x)?
But even if I take that last formula for granted, I have trouble making it agree with examples I give to GHCi. For example:
Prelude Control.Applicative> (pure (+5)) <*> (*3) $ 4 17 This expression instead appears consistent with f <*> g = \x -> f (g x) (note that in this version x doesn't appear after f.
I realize this is messy, so thanks for bearing with me.
In Haskell, an applicative is a parametrized type that we think of as being a container for data of that type plus two methods pure and <*> . Consider a parametrized type f a . The pure method for an applicative of type f has type. pure :: a -> f a. and can be thought of as bringing values into the applicative.
A function assigns to every element of a set X an element of a set Y. A functor assigns to every object of a category C an object of a category D and also assigns to every morphism in C a morphism in D in a way compatible with sources, targets, and composition.
Functor in Haskell is a kind of functional representation of different Types which can be mapped over. It is a high level concept of implementing polymorphism. According to Haskell developers, all the Types such as List, Map, Tree, etc. are the instance of the Haskell Functor.
A monad is a way of composing functions that require context in addition to the return value, such as computation, branching, or I/O. Monads type lift, flatten and map so that the types line up for lifting functions a => M(b) , making them composable.
First of all, remember how fmap is defined for applicatives:
fmap f x = pure f <*> x This means that your example is the same as (fmap (+ 5) (* 3)) 4. The fmap function for functions is just composition, so your exact expression is the same as ((+ 5) . (* 3)) 4.
Now, let's think about why the instance is written the way it is. What <*> does is essentially apply a function in the functor to a value in the functor. Specializing to (->) r, this means it applies a function returned by a function from r to a value returned by a function from r. A function that returns a function is just a function of two arguments. So the real question is this: how would you apply a function of two arguments (r and a, returning b) to a value a returned by a function from r?
The first thing to note is that you have to return a value of type (->) r which means the result also has to be a function from r. For reference, here is the <*> function:
f <*> g = \x -> f x (g x) Since we want to return a function taking a value of type r, x :: r. The function we return has to have a type r -> b. How can we get a value of type b? Well, we have a function f :: r -> a -> b. Since r is going to be the argument of the result function, we get that for free. So now we have a function from a -> b. So, as long as we have some value of type a, we can get a value of type b. But how do we get a value of type a? Well, we have another function g :: r -> a. So we can take our value of type r (the parameter x) and use it to get a value of type a.
So the final idea is simple: we use the parameter to first get a value of type a by plugging it into g. The parameter has type r, g has type r -> a, so we have an a. Then, we plug both the parameter and the new value into f. We need both because f has a type r -> a -> b. Once we plug both an r and an a in, we have a b1. Since the parameter is in a lambda, the result has a type r -> b, which is what we want.
Going through your original question, I think there's one subtle but very key point that you might have missed. Using the original example from LYAH:
(+) <$> (+3) <*> (*100) $ 5 This is the same as:
pure (+) <*> (+3) <*> (*100) $ 5 The key here is the pure before (+), which has the effect of boxing (+) as an Applicative. If you look at how pure is defined, you can see that to unbox it, you need to provide an additional argument, which can be anything. Applying <*> to (+) <$> (+3), we get
\x -> (pure (+)) x ((+3) x) Notice in (pure (+)) x, we are applying x to pure to unbox (+). So we now have
\x -> (+) ((+3) x) Adding (*100) to get (+) <$> (+3) <*> (*100) and apply <*> again, we get
\y -> (\x -> (+) ((+3) x)) y ((*100) y) {Since f <*> g = f x (g x)} 5 -> (\x -> (+) ((+3) x)) 5 ((*100) 5) (\x -> (+) ((+3) x)) 5 (500) 5 -> (+) ((+3) 5) (500) (+) 8 500 508 So in conclusion, the x after f is NOT the first argument to our binary operator, it is used to UNBOX the operator inside pure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With