Applying multiple functions to the same value point-free style in Haskell

后端 未结 4 640
醉酒成梦
醉酒成梦 2021-02-01 16:14

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

相关标签:
4条回答
  • 2021-02-01 16:37

    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)).

    0 讨论(0)
  • 2021-02-01 16:49

    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.

    0 讨论(0)
  • 2021-02-01 16:56

    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)
    
    0 讨论(0)
  • 2021-02-01 16:57

    Use sequence:

    > let foobar' = sequence [id, reverse]
    > foobar' "abcde"
    ["abcde","edcba"]
    
    0 讨论(0)
提交回复
热议问题