【问题标题】:Updating outer monad only in monad transformer仅在 monad 转换器中更新外部 monad
【发布时间】:2012-03-14 13:48:34
【问题描述】:

我有一个 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 不会解决问题。

【问题讨论】:

  • 感谢 danr 和 rampion 的回复。我选择使用 wrap 方法。虽然我可以更改 f2 的类型,但该函数出现在其他上下文中,因此我想编写它的类型而不参考特定调用函数的需求。

标签: haskell monads monad-transformers


【解决方案1】:

如果你定义了f2,最简单的方法可能是重构f2,所以它是这样定义的:

 f2 :: Monad m => WriterT [String] m b

这不应该太难,因为Writer w b 被定义为WriterT w Identity b,而Identity monad 不会给你任何东西。

然后你就可以通过 f1 &gt;&gt; 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 &gt;&gt; wrap f2a &gt;&gt; wrap f2b &gt;&gt; wrap f2c ...

【讨论】:

    【解决方案2】:

    作为对 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 编译指示:

    {-# LANGUAGE FlexibleContexts #-}
    

    一如既往,pragma 放在模块的顶部。

    在 cmets 中,rampion 给出了一个在此设置中包装函数的版本:

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

    【讨论】:

    • 包装器变成wrap :: MonadWriter w m =&gt; Writer w b -&gt; m b; wrap = uncurry (&lt;&lt;) . (return *** tell) . runWriter where (&lt;&lt;) = flip (&gt;&gt;)
    • 好吧,让我加入互敬社,并说我可以欣赏对 MonadWriter 类型类的概括:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-06
    • 2021-11-27
    • 1970-01-01
    • 2013-12-19
    • 2013-05-01
    • 1970-01-01
    相关资源
    最近更新 更多