Using the Applicative instance for functions:
parent :: Sheep -> Maybe Sheep
parent = mplus <$> mother <*> father
質問
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"?
解決
Using the Applicative instance for functions:
parent :: Sheep -> Maybe Sheep
parent = mplus <$> mother <*> father
他のヒント
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)
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!)