我有一个monad用于计算可能会失败并进行一些记录:
f1 :: WriterT [String] (Either String) a
我有一个不会失败的功能,但会做一些记录:
f2 :: Writer [String] b
使用f2中的日志更新f1中的writer monad的最佳方法是什么,并捕获f2计算的输出?目前我正在这样做:
f2result <- (\(r,l) -> do {tell l; return r}) (runWriter f2)
我使用lift用不同的计算来更新内部monad,因此切换Writer和Either monad都无法解决问题。
答案 0 :(得分:4)
如果您定义了f2
,那么最简单的方法可能是重构f2
,以便如此定义:
f2 :: Monad m => WriterT [String] m b
这不应该太难,因为Writer w b
被定义为WriterT w Identity b
,Identity
monad不会给你任何东西。
然后你就可以通过f1 >> f2
来链接它们。
如果您无法重新定义f2
,则可以随时使用相应的签名定义自己的:
f2' :: Monad m => WriterT [String] m b
f2' = WriterT . return $ runWriter f2
如果你有一堆f2
来包装,你可以随时定义一个函数来包装它们
wrap :: Monad m => Writer w b -> WriterT w m b
wrap = WriterT . return . runWriter
所以你可以做f1 >> wrap f2a >> wrap f2b >> wrap f2c ...
答案 1 :(得分:4)
作为对斜率回答的跟进,您可以改为重构f2
任何MonadWriter
:
f2 :: MonadWriter [String] m => m a
如果无法改变其定义,你可以 像rampion一样包装它:
f2' :: MonadWriter [String] m => m a
f2' = do let (a,w) = runWriter f2
tell w
return a
[String]
的{{1}}参数需要此GHC编译指示:
MonadWriter
与往常一样,编译指示放在模块的顶部。
在评论中,rampion给出了在此设置中包装函数的版本:
{-# LANGUAGE FlexibleContexts #-}