一些monad包装/展开/绑定混乱涉及包含Maybe的容器

时间:2013-08-02 21:36:11

标签: haskell monads monad-transformers

这里有一些示例代码

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))

我可能有点不清楚我想做什么,所以这里有更详细的描述:

  • 为了演示,所有有趣的事情都包含在计算上下文/容器中 - 在本例中为Identity。在我的实际代码中,我当然有另一个monad。

  • foogetStuffSometimes应该接受a类型的内容并返回包含在上下文中的Maybe a - 也就是说,它们要么返回Identity (Just a)(计算成功,或Identity Nothing(计算失败)。

  • getStuffAlways是永不失败的计算 - 它始终返回Identity a

  • 我希望foo

    1. 运行可用计算
    2. 如果可用计算失败,则失败(使用Nothing)
    3. 如果可用计算超过,则将其与getStuffAlways绑定,然后返回 Just (result of getStuffAlways to the result of #1)

这是Monad变形金刚的用例情况吗?在我的情况下,我的实际monad有点复杂,是我的图书馆在IO之上提供的多个变换器的堆栈,我不完全确定如何使它适用于这种情况(我问它在另一个问题中,最终我必须使用变形金刚)


跟进:

在现实生活中我有更多的东西:

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)

在这种情况下构建堆栈的最佳方法是什么?

1 个答案:

答案 0 :(得分:5)

是的,这是monad变换器的用例。给定类型的计算:

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

...您可以使用同名的MaybeT构造函数将其包装在MaybeT中:

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

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

然后你可以写:

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

... MaybeT将为您处理所有Nothing次检查,确保将它们穿过您的其他monad。

完成后,只需使用runMaybeT打开结果,在基本monad中返回一个返回Maybe的操作:

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

例如:

runMaybeT foo :: Identity (Maybe a)

编辑:要回答您的后续问题,您只需:

foo a = do
    firstPart <- getStuffAlways
    secondPart <- runMaybeT $ do
        stuff <- MaybeT getStuffSometimes a
        lift $ getStuffAlways stuff
    return (firstPart, secondPart)