简体   繁体   中英

How one parameter is used by two function simultaneously

I am reading about applicative functors and found such line:

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

It outputs 508.

How 5 could be used by (+3) and (*100) at the same time? Why don't we need to pass two 5's as a parameters like:

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

In the (->) a applicative instance, we find:

instance Applicative ((->) a) where
    pure = const
    (<*>) f g x = f x (g x)
    liftA2 q f g x = q (f x) (g x)

So, x is passed to both f and g by definition.

Here is a way to unbox it. We start with

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

(note that I left out the $ 5 ). The Applicative Functor whose <$> and <*> we are using here is the Function type (->) (partially applied to, I guess, Integer ). Here, the meaning of <$> and <*> is as follows:

f <$> g = \y -> f (g y)
g <*> h = \x -> g x (h x)

We can plug that in into the term in the first line and get

e = \x -> (\y -> (+) ((+3) y)) x ((*100) x

There are a few simplifications that we can do to this term:

e = \x -> (x+3) + (x*100)

So if this function is the value of (+) <$> (+3) <*> (*100) , then it should no longer be surprising that applying this to 5 gives

e 5 = (5+3) + (5*100) = 508

The thing is, you first have to understand how a function can be a functor. Think about a function like a container which reveals it's content only when you feed it with a parameter. In other words we only get to know it's content when this applicative functor (function) gets invoked with a parameter. So the type of this function would be say r -> a on two different types. However for functors we can only take an effect on a single type. So applicative functors are partially applied types just like the functor instance of Either type. We are interested in the output of the function so our applicative functor becomes (->) r in prefix notation. By remembering that <$> is the infix form of fmap

(+3) <$> (*2) $ 4 would result 11 . 4 is applied to our functor (*2) and the result (which is the value in the applicative functor context) gets mapped with (+3) .

However in our case we are fmap ing (+) to (+3) . To make it clearer lets rephrase the functions in lambda form.

(+) = \\wx -> w + x and (+3) = \\y -> y + 3 .

then (+) <$> (+3) by partially applying \\y -> y + 3 in the place of w our fmap applied applicative functor becomes \\yx -> (y + 3) + x .

Now here comes the applicative operator <*> . As mentioned in previous answers it is of definition g <*> h = \\x -> gx (hx) which takes a two parameter function g and partially applies g 's second parameter with it's second parameter function h . Now our operation looks like

(\\yx -> (y + 3) + x) <*> (*100) which can be rephrased as; (\\yx -> (y + 3) + x) <*> (\\z -> z*100) which means now we have to partially apply \\z -> z*100 to x and our function becomes \\yz -> (y + 3) + (z*100) .

Finally the applicative operator returns us a function which takes a single parameter and applies it to both parameters of the above two parameter function. So

\\x -> (\\yz -> (y + 3) + (z*100)) xx

Here is a slide deck dedicated to the subject of the ((->) r) instance of the Applicative type class: https://www2.slideshare.net/pjschwarz/function-applicative-for-great-good-of-palindrome-checker-function-polyglot-fp-for-fun-and-profit-haskell-and-scala

And here are a couple of slides from it: 在此处输入图片说明 在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM