4

I am playing with Parsec and I want to combine two parsers into one with the result put in a pair, and then feed it another function to operate on the parse result to write something like this:

try (pFactor <&> (char '*' *> pTerm) `using` (*))

So I wrote this:

(<&>) :: (Monad m) => m a -> m b -> m (a, b)
pa <&> pb = do
  a <- pa
  b <- pb
  return (a, b)

And

using :: (Functor f) => f (a, b) -> (a -> b -> c) -> f c
p `using` f = (uncurry f) <$> p

Is there anything similar to (<&>) which has been implemented somewhere? Or could this be written pointfree? I tried fmap (,) but it seems hard to match the type.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Lin Jen-Shin
  • 172
  • 8
  • Andy Gill calls this the `both` combinator in documentation on Chalkboard and Kansas-Lava. I haven't seen it defined anywhere, but I have defined it myself at least once. – stephen tetley Sep 24 '11 at 19:50

5 Answers5

11

Better than <&> or liftM2 would be

(,) <$> a <*> b

since Applicative style seems to be gaining popularity and is very succinct. Using applicative style for things like this will eliminate the need for <&> itself, since it is much clearer than (,) <$> a <*> b.

Also this doesn't even require a monad - it will work for Applicatives too.

alternative
  • 12,703
  • 5
  • 41
  • 41
  • 2
    And you don't need `using`, either: you can just use `(*)` instead of `(,)` as in `(*) <$> a <*> b`. – Daniel Wagner Sep 24 '11 at 20:33
  • Thank you, I ended up with `liftA2 (,)`. I still want `<&>` and `using` though, because I want to change the order of arguments. i.e. putting `(*)` on the end. – Lin Jen-Shin Sep 24 '11 at 21:23
7

Is there anything similar to (<&>) which has been implemented somewhere? Or could this be written pointfreely? I tried fmap (,) but it seems hard to match the type.

I don't now if it's implemented anywhere, but <&> should be the same as liftM2 (,). The difference to fmap is, that liftM2 lifts a binary function into the monad.

luqui
  • 59,485
  • 12
  • 145
  • 204
bzn
  • 2,362
  • 1
  • 17
  • 20
6

Using applicative style, there is no need to put the intermediate results into a tuple just to immediately apply an uncurried function. Just apply the function "directly" using <$> and <*>.

try ((*) <$> pFactor <*> (char '*' *> pTerm))

In general, assuming sane instances of Monad and Applicative,

do x0 <- m0
   x1 <- m1
   ...
   return $ f x0 x1 ...

is equivalent to

f <$> m0 <*> m1 <*> ...

except that the latter form is more general and only requires an Applicative instance. (All monads should also be applicative functors, although the language does not enforce this).

hammar
  • 138,522
  • 17
  • 304
  • 385
  • Actually this is where I came from. :) But I want to put `(*)` on the end, thus introducing `<&>` and `using`. I ended up with `liftA2 (,)` though. Yeah, if Applicative could do the job, then why not? – Lin Jen-Shin Sep 24 '11 at 21:25
3

Note, that if you go the opposite direction from Applicative you'll see that the way you want to combine parsers fits nicely into Arrow paradigm and Arrow parsers implementation. E.g.:

import Control.Arrow

(<&>) = (&&&) 

p `using` f = p >>^ uncurry f 
Ed'ka
  • 6,595
  • 29
  • 30
  • Interesting... it seems I need to rewrite the parsers to fit into this though? – Lin Jen-Shin Sep 25 '11 at 15:37
  • Yes, Parsec is monadic not arrow parser (unlike `PArrows`). I am not sure how widely used arrow parser are (I suspect not very widely) though they are supposed to be more efficient (but probably harder for understanding as well). – Ed'ka Sep 28 '11 at 14:31
1

Yes, you could use applicative style but I don't believe that answers either of your questions.

Is there some already defined combinator that takes two arbitrary monads and sticks their value types in a pair and then sticks that pair in the context?

Not in any of the standard packages.

Can you do that last bit point free?

I'm not sure if there is a way to make a curried function with an arity greater than 1 point free. I don't think there is.

Hope that answers your questions.

ExternalReality
  • 543
  • 4
  • 14
  • The `((.) . (.))` combinator is often used to make a function with arity greater than 1 point free. But using auxiliary functions is even better! – alternative Sep 24 '11 at 20:43
  • 2
    Thank you for your direct answer. :) I don't understand why curried function with an arity greater than 1 can't be point free though. Isn't `(<&>) = liftA2 (,)` point free? Can you elaborate or give me some reference? Thanks! – Lin Jen-Shin Sep 24 '11 at 21:28