Reputation: 343
Suppose I have two functions
f :: Monad m => a -> m a
g :: a -> a
that I want to consecutively apply to some element like so:
(return x) >>= f >>= g
This doesn't work because g is pure, so I first need to "artificially" turn it monadic. One possibility is
(return x) >>= f >>= (return . g)
which isn't very intuitive to me. Another possibility is to use that a Monad is Applicative:
(return g) <*> ((return x) >>= f)
But this is not very intuitive because of the different order of function and argument:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
What is the canonical way to deal with this? If (>>==) = (flip ((<*>) . pure))
one can write ((pure x) >>= f) >>== g
, which would be fine except for operator precedence. Of course pure functions in monadic code are a common thing so surely there is a standard way to deal with them?
Edit: I didn't say this originally but I was thinking of a situation where I had several functions, some pure, some monadic, and I wanted to apply them in some random order.
Upvotes: 4
Views: 241
Reputation: 5063
I think the most straightforward solution is Kleisli composition >=>
from Control.Monad
module.
Since both >=>
and (.)
are right-associative and (.)
has higher precedence, you can write the following:
-- (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
f :: Monad m => a -> m a
g :: a -> a
h :: Monad m => a -> m b
j :: b -> b
q :: Monad m => a -> m b
q = f >=> return . g >=> h >=> return . j
-- | |- Use (return .) to transform (a -> b) into (a -> m b)
-- |- Use Kleisli composition >=> to compose Kleisli arrows, i.e.
-- |- the functions going from values to monadic values, (a -> m b)
Upvotes: 5
Reputation: 531165
You can give your (>>==)
operator an appropriate precedence to allow the parentheses to be dropped
(>>==) = flip ((<*>) . pure)
infixl 1 >>==
Then
> (>>==) = flip ((<*>) . pure); infixl 1 >>==
> pure 3 >>= (\x -> [x, x]) >>== (+1)
[4,4]
Upvotes: 4
Reputation: 343
A solution that I found thanks to @WillemVanOnsems pointer to Control.Monad and that is in the spirit of what I was looking for is to use =<<
which has the same argument order as <*>
and <$>
:
g <$> (f =<< f =<< g <$> g <$> (f =<< y))
where now y
is already monadic (say return x
). The only thing I am not happy about are the parentheses due to operator precedence.
Upvotes: 1
Reputation: 476604
What you here describe is fmap :: Functor f => (a -> b) -> f a -> f b
. Furthermore since pure x >>= f
should be the same as f x
, we thus can simplify the given expression to:
fmap g (f x)
or we can make use of the infix alias (<$>) :: Functor f => (a -> b) -> f a -> f b
:
g <$> f x
Upvotes: 11