Reputation: 450
There are functions with signatures like:
a -> IO (m b)
b -> IO (m c)
c -> IO (m d)
How do I chain them in
a -> IO (m d)
?
Practical application: say there a set of REST endpoints. Each of the return a value and next one is requires the value returned by previous as an argument.
So functions for fetching from the endpoints are like:
Value1 -> IO (Maybe Value2)
Value2 -> IO (Maybe Value3)
Value3 -> IO (Maybe Value4)
Upvotes: 1
Views: 496
Reputation: 322
if m has an instance of Traversable
(Maybe
has one) here is another alternative:
import Control.Monad (join)
f :: (Traversable m, Monad m)
=> (a -> IO (m b))
-> (b -> IO (m c))
-> (c -> IO (m d))
-> a
-> IO (m d)
f fa fb fc a = fa a
>>= traverse fb
>>= fmap join . traverse fc . join
Upvotes: 2
Reputation: 32319
There are functions with signatures like:
a -> IO (m b) b -> IO (m c) c -> IO (m d)
How do I chain them in
a -> IO (m d)
In general, you might not be able to. For example, if m
is Const
, I'm not sure asking this even makes sense. In general, you probably expect m
to be a Monad
. However, note that even if m
is a Monad
, its composition with IO
may not be (see this).
Value1 -> IO (Maybe Value2) Value2 -> IO (Maybe Value3) Value3 -> IO (Maybe Value4)
Ah, Now you are talking! The abstraction you are looking for here is MaybeT
and Kleisli composition (>=>)
. Then, for example,
import Control.Monad ((>=>))
import Control.Monad.Trans.Maybe (MaybeT(..))
rest1 :: Value1 -> IO (Maybe Value2)
rest2 :: Value2 -> IO (Maybe Value3)
rest3 :: Value3 -> IO (Maybe Value4)
rest4 :: Value1 -> IO (Maybe Value4)
rest4 x = runMaybeT ((MaybeT . rest1 >=> MaybeT . rest2 >=> MaybeT . rest3) x)
That still looks a bit ugly. The thing to do is probably to refactor your rest1
, rest2
, and rest3
functions. As has been pointed out in the comments MaybeT IO a
can be converted to and from IO (Maybe a)
(in fact that is exactly what runMaybeT
and MaybeT
do).
import Control.Monad ((>=>))
import Control.Monad.Trans.Maybe (MaybeT(..))
rest1 :: Value1 -> MaybeT IO Value2
rest2 :: Value2 -> MaybeT IO Value3
rest3 :: Value3 -> MaybeT IO Value4
rest4 :: Value1 -> MaybeT IO Value4
rest4 = rest1 >=> rest2 >=> rest3
Upvotes: 6