michaelmesser
michaelmesser

Reputation: 3726

How to deal with an IO (Maybe (IO (Maybe t))) type?

I am dealing with a library (ghcjs-dom) in which every function returns an IO (Maybe T).

I have a function a with returns an IO (Maybe x) and function b which takes x as an argument and returns an IO (Maybe y).

Is an operator that lets me do a ??? b and get an IO (Maybe y). My Hoogle search turned up nothing.

I am looking something like join that works for IO (Maybe (IO (Maybe t))) instead of IO (IO t) or Maybe (Maybe t).

Upvotes: 5

Views: 1406

Answers (2)

ErikR
ErikR

Reputation: 52049

From what I understand, you have:

a :: IO (Maybe X)
b :: X -> IO (Maybe Y)

There is a close relationship between IO (Maybe a) and MaybeT IO a, namely MaybeT converts one to the other:

MaybeT :: IO (Maybe a) -> MaybeT IO a

and the inverse operation is just runMaybeT:

runMaybeT :: MaybeT IO a -> IO (MaybeT a)

In the MaybeT monad the composition you want to perform is just the bind operation:

MaybeT a >>= (\x -> MaybeT (b x)) :: MaybeT IO Y

This results in a value of type MaybeT IO Y. To convert it back to a IO (Maybe Y) just use runMaybeT.

Update

Here is an operator to "compose" a and b:

andThen :: IO (Maybe a) -> (a -> IO (Maybe b)) -> IO (Maybe b)
andThen a b = runMaybeT $  MaybeT a >>= (\x ->  MaybeT (b x) )

However, if you find yourself using this operator a lot, perhaps you should rework your functions so you work primarily in the MaybeT IO monad, and then you can just use >>= with a single runMaybeT on the outside.

Upvotes: 11

mb14
mb14

Reputation: 22616

If you don't want to use MaybeT what you need is sequenceA or traverse from Data.Traversable.

Prelude Data.Traversable Control.Monad> :t fmap join . join . fmap sequenceA 

fmap join . join . fmap sequenceA
  :: (Traversable m, Control.Applicative.Applicative f, Monad m,
      Monad f) =>
      f (m (f (m a))) -> f (m a)

In your case f is IO and m Maybe.

Upvotes: 4

Related Questions