Haskell 仅在monad transformer中更新外部monad

Haskell 仅在monad transformer中更新外部monad,haskell,monads,monad-transformers,Haskell,Monads,Monad Transformers,我有一个monad用于可能失败的计算,并执行一些日志记录: f1 :: WriterT [String] (Either String) a f2 :: Writer [String] b 我有一个不会失败的功能,但会进行一些日志记录: f1 :: WriterT [String] (Either String) a f2 :: Writer [String] b 使用f2中的日志更新f1中的writer monad并捕获f2计算的输出的最佳方法是什么?目前我正在这样做: f2resul

我有一个monad用于可能失败的计算,并执行一些日志记录:

f1 :: WriterT [String] (Either String) a
f2 :: Writer [String] b
我有一个不会失败的功能,但会进行一些日志记录:

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)
f2result do{tell;return r}(runWriter f2)

我正在使用lift使用不同的计算更新内部单子,因此切换Writer和任一单子都不能解决问题。

如果定义了
f2
,最简单的方法可能是重构
f2
,从而定义它:

 f2 :: Monad m => WriterT [String] m b
这应该不会太难,因为
Writer w b
被定义为
Writer w Identity b
,而
Identity
单子不会给你任何东西

然后,只需执行
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…

作为rampion答案的后续操作,您可以改为重构
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
MonadWriter
[String]
参数需要以下GHC pragma:

{-# LANGUAGE FlexibleContexts #-}
与往常一样,pragma放在模块的顶部

在评论中,rampion给出了在此设置中包装函数的版本:

wrap :: MonadWriter w m => Writer w b -> m b
wrap = uncurry (<<) . (return *** tell) . runWriter 
  where (<<) = flip (>>)
wrap::MonadWriter w m=>Writer w b->m b

wrap=uncurry(包装器变成
wrap::MonadWriter w m=>Writer w b->mb;wrap=uncurry(好吧,让我加入相互敬佩协会,并说我很欣赏MonadWriter typeclass的泛化:)感谢danr和rampion的回复。我选择使用wrap方法。虽然我可以更改f2的类型,但该函数会出现在其他上下文中,因此我希望在编写其类型时不参考特定调用函数的需要。