1

I'm going through some Haskell tutorials and trying to get familiar with the language. I've seen this example in a Monad/MonadPlus tutorial:

data Sheep = Sheep {name :: String, mother :: Maybe Sheep, father :: Maybe Sheep}
parent :: Sheep -> Maybe Sheep
parent s = mother s `mplus` father s

I was trying to rewrite it in point-free style (just as an exercise, not saying that the above is wrong or non-idiomatic), but I got stuck: obviously I can write a custom function

partialPlus :: (MonadPlus m) => (a -> m b) -> (a -> m b) -> a -> m b
partialPlus f1 f2 = \x -> f1 x `mplus` f2 x

and then use

parent = partialPlus mother father

but I seem to remember from the LYAH tutorial that there was a way to use functors or applicative functors to construct tree of computations that can be finally fed arguments to get the result out of the "functor-box". However I can't seem to find the example in the tutorial. How do I rewrite the above "cleverly"?

BruceBerry
  • 1,166
  • 1
  • 9
  • 21

2 Answers2

5

Using the Applicative instance for functions:

parent :: Sheep -> Maybe Sheep
parent = mplus <$> mother <*> father
hammar
  • 138,522
  • 17
  • 304
  • 385
  • 1
    what the hell, i swear this is what came to my mind and tried first :D not sure why it didn't work. thanks! – BruceBerry Jun 10 '13 at 17:59
  • bonus: how to write `grandparent :: Sheep -> Maybe Sheep` in point-free style? It should be something like `mplus <$> (parent ??? mother) <*> (parent ??? father)`, where `???` is again applicative or monadic, so that you propagate Nothing correctly. It seems that I always get stuck when I want to be clever... – BruceBerry Jun 10 '13 at 19:56
  • 2
    @BruceBerry: You can use the `>=>` (aka. Kleisli composition) operator, which is like `flip (.)` with monads, so `grandparent = parent >=> parent`. – hammar Jun 10 '13 at 19:58
  • thanks. it seems that most monad tutorials do not introduce this operator, though i've seen it a bunch of times already. again, i'm probably trying to be too clever for my own good, and `grandparent s = (father s >>= parent) \`mplus\` (mother s >>= parent)` is perfectly fine, but it seems that haskell library writers do not really embrace simplicity, so I have some work do to before I can understand their code :-) – BruceBerry Jun 10 '13 at 20:09
4

Very point-free

You could write it as

partialPlus :: MonadPlus m => (t -> m a) -> (t -> m a) -> t -> m a
partialPlus = liftM2 mplus

this works because of the monad instance for (->) t (which you can think of as meaning (t ->).

The types in more detail:

liftM2 :: Monad func_t => (a -> b -> c) -> func_t a -> func_t b -> func_t c

Giving it the highly prejudicial name func_t to suggest function from t to...

so then

liftM2 mplus :: (Monad func_t, MonadPlus m) => func_t (m a) -> func_t (m a) -> func_t (m a)

But why use Monad when you can have the nicer Applicative?

Or Why I stole hammar's nice answer:

Now, there's an applicative instance for (->) t and so we could just as well have written

partialPlus = liftA2 mplus

which works for exactly the same reason. But that's great news, because if you can use liftA2 or liftA3 etc, you can do it with the awesome <$> and <*> from Control.Applicative.

In general, if you have

this = do
  x <- mx
  y <- my
  z <- mz
  return (f x y z)

that's expressible as liftM3 f mx my mz, but more nicely as f <$> mx <*> my <*> mz, especially if mx and my etc are actually more complicated expression, as I find usually. <*> and <*> have low precedence (4) so you don't need brackets most of the time.

In this case we can have

partialPlus f1 f2 = mplus <$> f1 <*> f2

which is point-free. (...as long as you consider t -> m a as not a point!)

AndrewC
  • 32,300
  • 7
  • 79
  • 115