开发者

Trick for "reusing" arguments in Haskell?

开发者 https://www.devze.com 2023-01-29 00:11 出处:网络
From time to time I stumble over the problem that I want to express \"please use the last argument twice\", e.g. in order to write pointfree style or to avoid a lambda. E.g.

From time to time I stumble over the problem that I want to express "please use the last argument twice", e.g. in order to write pointfree style or to avoid a lambda. E.g.

sqr x = x * x

could be written as

sqr = doubleArgs (*) where
   doubleArgs f x = f x x

Or consider this slightly more complicated function (taken from this question):

ins x xs = zipWith (\ a b -> a ++ (x:b)) (inits xs) (tails xs)

I could write this code pointfree 开发者_JS百科if there were a function like this:

ins x = dup (zipWith (\ a b -> a ++ (x:b))) inits tails where
     dup f f1 f2 x = f (f1 x) (f2 x)

But as I can't find something like doubleArgs or dup in Hoogle, so I guess that I might miss a trick or idiom here.


From Control.Monad:

join :: (Monad m) -> m (m a) -> m a
join m = m >>= id

instance Monad ((->) r) where
    return = const
    m >>= f = \x -> f (m x) x

Expanding:

join :: (a -> a -> b) -> (a -> b)
join f = f >>= id
       = \x -> id (f x) x
       = \x -> f x x

So, yeah, Control.Monad.join.

Oh, and for your pointfree example, have you tried using applicative notation (from Control.Applicative):

ins x = zipWith (\a b -> a ++ (x:b)) <$> inits <*> tails

(I also don't know why people are so fond of a ++ (x:b) instead of a ++ [x] ++ b... it's not faster -- the inliner will take care of it -- and the latter is so much more symmetrical! Oh well)


What you call 'doubleArgs' is more often called dup - it is the W combinator (called warbler in To Mock a Mockingbird) - "the elementary duplicator".

What you call 'dup' is actually the 'starling-prime' combinator.

Haskell has a fairly small "combinator basis" see Data.Function, plus some Applicative and Monadic operations add more "standard" combinators by virtue of the function instances for Applicative and Monad (<*> from Applicative is the S - starling combinator for the functional instance, liftA2 & liftM2 are starling-prime). There doesn't seem to be much enthusiasm in the community for expanding Data.Function, so whilst combinators are good fun, pragmatically I've come to prefer long-hand in situations where a combinator is not directly available.


Here is another solution for the second part of my question: Arrows!

import Control.Arrow

ins x = inits &&& tails >>> second (map (x:)) >>> uncurry (zipWith (++))

The &&& ("fanout") distributes an argument to two functions and returns the pair of the results. >>> ("and then") reverses the function application order, which allows to have a chain of operations from left to right. second works only on the second part of a pair. Of course you need an uncurry at the end to feed the pair in a function expecting two arguments.

0

精彩评论

暂无评论...
验证码 换一张
取 消