functions as applicative functors (Haskell / LYAH)

后端 未结 4 1140
既然无缘
既然无缘 2020-12-02 09:58

Chapter 11 of Learn You a Haskell introduces the following definition:

instance Applicative ((->) r) where
    pure x = (\\_ -> x)
    f <*         


        
4条回答
  •  南方客
    南方客 (楼主)
    2020-12-02 10:24

    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.

提交回复
热议问题