Pergunta

Here's a bit of sample code

foo :: a -> Identity (Maybe a)
foo a = do
  maybeStuff <- getStuffSometimes a
  return $ case maybeStuff of                  -- this "case" stuff is the kind
    Just stuff -> Just $ getStuffAlways stuff  -- of code I'd expect the Maybe
    Nothing -> Nothing                         -- monad to help with

getStuffSometimes :: a -> Identity (Maybe a)
getStuffSometimes a = return $ Just a

getStuffAlways :: a -> Identity a
getStuffAlways = return

-- ERROR (on the return statement of the do block)
-- Expected type: Identity (Maybe a)
--   Actual type: Identity (Maybe (Identity a))

It might be a little unclear what I'm trying to do, so here's a more detailed description:

  • All of the interesting things are wrapped in a computational context/container -- in this case, Identity, for the sake of demonstration. In my actual code I have another monad of course.

  • foo and getStuffSometimes are supposed to take in something of type a and return a Maybe a wrapped in the context -- that is, they either return Identity (Just a) (the computation successful, or Identity Nothing (the computation failed).

  • getStuffAlways is a computation that never fails -- it always returns Identity a.

  • I want foo to:

    1. Run a failable computation
    2. If the failable computation fails, fail (with Nothing)
    3. If the failable computation suceeds, bind it with getStuffAlways, and return Just (result of getStuffAlways to the result of #1)

Is this a use case situation for Monad Transformers? In my case my actual monad is a bit complicated and is a stack of multiple transformers given by my library on top of IO and i'm not totally sure how to make it work for this situation (I'd ask it in another question of it ends up being that I have to use transformers)


Follow up:

In real life I have something more like:

foo :: a -> Identity (a, Maybe a)
foo a = do
  firstPart <- getStuffAlways a

  maybeStuff <- getStuffSometimes a
  secondPart <- case maybeStuff of
    Just stuff -> Just $ getStuffAlways stuff
    Nothing -> Nothing

  return (firstPart, secondPart)

What would be the best way to construct the stack in this case?

Foi útil?

Solução

Yes, this is a use case for monad transformers. Given a computation of type:

computation :: (Monad m) => m (Maybe a)

... you can wrap it in MaybeT using the MaybeT constructor of the same name:

MaybeT :: (Monad m) => m (Maybe a) -> MaybeT m a

MaybeT computation :: (Monad m) => MaybeT m a

Then you can just write:

foo :: MaybeT Identity a
foo = do
   stuff <- MaybeT getStuffSometimes
   lift $ getStuffAlways stuff

... and MaybeT will take care of all the Nothing checks for you, making sure to thread them through your other monad.

When your done, just use runMaybeT to unwrap the result, giving back an action in the base monad that returns a Maybe:

runMaybeT :: (Monad m) => MaybeT m a -> m (Maybe a)

For example:

runMaybeT foo :: Identity (Maybe a)

Edit: To answer your follow up question, you would just do:

foo a = do
    firstPart <- getStuffAlways
    secondPart <- runMaybeT $ do
        stuff <- MaybeT getStuffSometimes a
        lift $ getStuffAlways stuff
    return (firstPart, secondPart)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top