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