Write f in pointfree-style?

穿精又带淫゛_ 提交于 2019-12-05 16:05:03
Lynn

We have:

k x y = (f (g x)) (h y)

and we wish to write k in point-free style.

The first argument passed to k is x. What do we need to do with x? Well, first we need to call g on it, and then f, and then do something fancy to apply this to (h y).

k = fancy . f . g

What is this fancy? Well:

k x y = (fancy . f . g) x y
      = fancy (f (g x)) y
      = f (g x) (h y)

So we desire fancy z y = z (h y). Eta-reducing, we get fancy z = z . h, or fancy = (. h).

k = (. h) . f . g

A more natural way to think about it might be

                             ┌───┐           ┌───┐
                        x ───│ g │─── g x ───│   │
                      /      └───┘           │   │
               (x, y)                        │ f │─── f (g x) (h y)
                      \      ┌───┐           │   │
                        y ───│ h │─── h y ───│   │
                             └───┘           └───┘

                      └──────────────────────────────┘
                                      k

Enter Control.Arrow:

k = curry ((g *** h) >>> uncurry f)

Take a look at online converter

It converted

f' x y = f (g x) (h y) 

into

f' = (. h) . f . g

with the flow of transformations

f' = id (fix (const (flip ((.) . f . g) h))) 
f' = fix (const (flip ((.) . f . g) h)) 
f' = fix (const ((. h) . f . g)) 
f' = (. h) . f . g

This is slightly longer, but a little easier to follow, than (. h) . f. g.

First, rewrite f' slightly to take a tuple instead of two arguments. (In otherwords, we're uncurrying your original f'.)

f' (x, y) = f (g x) (h y)

You can pull a tuple apart with fst and snd instead of pattern matching on it:

f' t = f (g (fst t)) (h (snd t))

Using function composition, the above becomes

f' t = f ((g . fst) t) ((h . snd) t)

which, hey, looks a lot like the version you could make point-free using applicative style:

f' = let g' = g . fst
         h' = h . snd
     in (f <$> g') <*> h'

The only problem left is that f' :: (a, a) -> d. You can fix this by explicitly currying it:

f' :: a -> a -> d
f' = let g' = g . fst
         h' = h . snd
     in curry $ (f <$> g') <*> h'

(This is very similar, by the way, to the Control.Arrow solution added by Lynn.)

Will Ness

Using the "three rules of operator sections" as applied to the (.) function composition operator,

(.) f g  =  (f . g)  =  (f .) g  =  (. g) f   -- the argument goes into the free slot
--       1           2           3

this is derivable by a few straightforward mechanical steps:

k x y =  (f (g x)) (h y)                      -- a (b c) === (a . b) c
      =  (f (g x) . h) y
      =  (. h)  (f (g x)) y
      =  (. h)  ((f . g)  x) y
      = ((. h) . (f . g)) x  y

Lastly, (.) is associative, so the inner parens may be dropped.

The general procedure is to strive to reach the situation where eta-reduction can be performed, i.e. we can get rid of the arguments if they are in same order and are outside any parentheses:

k x y = (......) y
=>
k x   = (......)

Lather, rinse, repeat.


Another trick is to turn two arguments into one, or vice versa, with the equation

curry f x y = f (x,y)     

so, your

f (g x) (h y) = (f.g) x (h y)                      -- by B-combinator rule
              = (f.g.fst) (x,y) ((h.snd) (x,y))
              = (f.g.fst <*> h.snd) (x,y)          -- by S-combinator rule
              = curry (f.g.fst <*> h.snd) x y

This is the same as the answer by @chepner, but presented more concisely.

So, you see, your (f.g <*> h) x1 just becomes (f.g.fst <*> h.snd) (x,y). Same difference.


1(because, for functions, (<$>) = (.))

Control.Compose

(g ~> h ~> id) f

Data.Function.Meld

f $* g $$ h *$ id

Data.Function.Tacit

lurryA @N2 (f <$> (g <$> _1) <*> (h <$> _2))
lurryA @N5 (_1 <*> (_2 <*> _4) <*> (_3 <*> _5)) f g h

Related articles

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!