这里有一些示例代码
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。
foo
和getStuffSometimes
应该接受a
类型的内容并返回包含在上下文中的Maybe a
- 也就是说,它们要么返回Identity (Just a)
(计算成功,或Identity Nothing
(计算失败)。
getStuffAlways
是永不失败的计算 - 它始终返回Identity a
。
我希望foo
:
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)
在这种情况下构建堆栈的最佳方法是什么?
答案 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)