21

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

Community
  • 1
  • 1
Landei
  • 54,104
  • 13
  • 100
  • 195

3 Answers3

30

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)

luqui
  • 59,485
  • 12
  • 145
  • 204
  • 2
    And according to `pointfree`, `dup` works out to be `liftM2`. I really need to get a better handle on the monad instance for functions. – Antal Spector-Zabusky Dec 02 '10 at 10:30
  • 2
    Thank you both for giving even **two** approaches to solve such problems. BTW I tried `sqr = (*) <$> id <*> id` and it works as well :-) – Landei Dec 02 '10 at 10:57
  • `a ++ (x:b)` is 3 characters shorter than your alternative, maybe that's why some people prefer it? – John L Dec 02 '10 at 14:13
  • 1
    If I would like to emphasize symmetry, I'd rather write `concat [a,[x],b]` instead of `a ++ [x] ++ b` – Landei Dec 02 '10 at 15:22
  • 2
    @Antal S-Z: There's really not that much to it--just a lightweight Reader monad that's easy to use inline. The first argument serves as the environment, `fmap` and `return` are independent of the environment as you'd expect, etc. One of my favorite uses is with a conditional combinator `(>)` that can be used like `even > (\`div\` 2) <*> (+ 1)` which I think is much more readable than `\n -> if even n then n `div` 2 else n + 1`. (n.b. -- `liftM2 (\b t e -> if b then t else e)` will produce side effects from both branches, though this is irrelevant to `Reader`) – C. A. McCann Dec 02 '10 at 17:25
  • This answer helped me intuitively understand what `join` does with a function. Thank you. – N3dst4 Oct 18 '17 at 07:23
12

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.

stephen tetley
  • 4,465
  • 16
  • 18
  • 2
    Oh, I found the "bird-operators" for Haskell: http://hackage.haskell.org/packages/archive/data-aviary/0.2.3/doc/html/Data-Aviary-Birds.html – Landei Dec 02 '10 at 12:41
  • 2
    @Landei - I consider them "reference only", i.e. I wouldn't recommend depending on them in working code. I ought to make the Cabal description more explicit that they are "reference only", but I haven't gotten round to it yet. – stephen tetley Dec 02 '10 at 12:56
  • 2
    What @Landei calls `dup` is also known as [a "verb fork" in J](http://www.jsoftware.com/help/jforc/forks_hooks_and_compound_adv.htm), where it's written by simple juxtaposition of operators, e.g. `(f g h) x` instead of `dup f g h x`. – C. A. McCann Dec 02 '10 at 17:35
9

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.

Landei
  • 54,104
  • 13
  • 100
  • 195