Applying multiple functions to the same value poin

2019-03-09 17:55发布

问题:

I was bored one day and wanted to exercise my brain, so I decided to do the 99 Haskell Problems but restricted myself to doing them in point-free style. A problem that seems to crop up a lot when I'm doing things in point-free style is this: How do you apply multiple functions to the same value while keeping each result as an independent entity? Using pointed notation:

foobar x = [id x, reverse x]

And what I've come up with so far in point-free notation:

foobar' = `map` [id, reverse] ($ x)

I can't seem to get that x off the end of there.

回答1:

Others have already posted how you can do this using the Reader monad, but that's not the only way. It turns out that your second function is pretty close. I think you meant to post

foobar' x = (`map` [id, reverse]) ($ x)

Since the x is already near a rightmost position, you're almost there. First, transform the section ($ x) into a function, because it's a bit easier to work with:

-- by the definition of a right operator section
foobar'2 x = (`map` [id, reverse]) (\y -> ($) y x)

Next remove the x from the lambda body by bringing a new variable into scope, and applying the function to x

-- lambda abstraction I think...
foobar'2 x = (`map` [id, reverse]) $ (\z y -> ($) y z) x

Rewrite this application as a function composition, and then you can eta reduce:

-- by definition of '.'
foobar'3 x = (`map` [id, reverse]) . (\z y -> ($) y z) $ x

-- eta reduction
foobar'4 = (`map` [id, reverse]) . (\z y -> ($) y z)

Finally, notice that we can replace the lambda with a function

-- by definition of `flip`
foobar'5 = (`map` [id,reverse]) . flip ($)

and you have a point-free form.



回答2:

You will be interested in the Applicative instance of the reader monad:

instance Applicative (e ->)

Using it you can easily distribute an argument:

liftA2 (+) sin cos 3

Here sin and cos are functions, which both receive the value 3. The individual results are then combined using (+). You can further combine this with the Category instance of (->), but of cource specialized versions of (.) and id are already defined in the Prelude.

Background: The Applicative instance for (e ->) really represents the SKI calculus, where (<*>) is the S combinator and pure is the K combinator. S is precisely used to distribute an argument to two functions:

S f g x = f x (g x)

It takes a function application (f g) and makes both dependent on the value x ((f x) (g x)).



回答3:

Use sequence:

> let foobar' = sequence [id, reverse]
> foobar' "abcde"
["abcde","edcba"]


回答4:

There are a few basic idiomatic combinators which pop up repeatedly, and are reimplemented with various higher concepts and libraries, but which are essentially very simple. Names may vary, and some are implementable in terms of others:

fork (f,g) x = (f x, g x)              -- == (f &&& g)
prod (f,g) x = (f $ fst x, g $ snd x)  -- == (f *** g)
pmap f (x,y) = (f x, f y)              -- == (f *** f)
dup     x    = (x,x)

etc. Of course uncurry f (x,y) == f x y gets used a lot with these, too.

&&& and *** are defined in Control.Arrow, as well as first and second. Then prod (f,id) == first f, prod(id,g) == second g etc. etc.

So your foobar becomes

foobar = (\(a,b)->[a,b]) . fork (id,reverse)
       = (\(a,b)->[a,b]) . (id &&& reverse)
       = (\(a,b)->[a,b]) . (id *** reverse) . dup 
       = join $ curry ( (\(a,b)->[a,b]) . second reverse)

For the last one you need to also import Control.Monad and Control.Monad.Instances. See also this question.


late edit: also, using Control.Applicative as hinted in answer by ertes,

       = (:) <*> ((:[]) . reverse)