【问题标题】:Swapping `mappend` in Writer monad在 Writer monad 中交换`mappend`
【发布时间】:2017-04-19 01:59:45
【问题描述】:

总结:在使用 Writer monad 时,我希望能够在 mappend 的 2 个不同版本之间切换而不会丢失状态。

我使用两个布尔标志来跟踪某些状态:

data Flags = F Bool Bool

现在我定义了两个 Monoid 实例,它们在组合 mappend 中的标志的方式上有所不同:

newtype XFlags = XF Flags

instance Monoid XFlags where
  mempty = XF (F True False)
  (XF (F s0 c0)) `mappend` (XF (F s1 c1)) = XF (F (s0 && s1)
  (c0 || c1 || not (s0 || s1)))

newtype SFlags = SF Flags

instance Monoid SFlags where
  mempty = SF (F True False)
  (SF (F s0 c0)) `mappend` (SF (F s1 c1)) = SF (F (s0 && s1) (c0 || c1))

现在我可以拥有 2 个具有不同标志处理的 Writer monad:

type XInt = WriterT XFlags Identity Int
type SInt = WriterT SFlags Identity Int

现在我可以进行如下操作:

xplus :: XInt -> XInt -> XInt
xplus = liftM2 (+)

splus :: SInt -> SInt -> SInt
splus = liftM2 (+)

现在我想构建如下表达式:

foo = splus (return 1) (xplus (return 2) (return 3))

为此,我需要能够在两者之间进行转换而不会丢失任何标志,最好不要打开 monad(使用 runWriter)。这部分我还没有弄清楚。看起来有点像我可以尝试使用 monad 转换器嵌套 Writers,但我不确定它是否直接适用于这里。我将不胜感激有关实施此类事情的最佳方法的一些指导。

【问题讨论】:

  • 你为什么不想打开 monad?
  • 理想情况下,我希望 monad 跟踪整个计算树。稍后我可以使用不同的 Monoid 来构建跟踪或进行一些其他分析。隐式打开它会打破这一点(我认为)。完全披露:上面的例子是在 Haskell 中,但我实际上是在 Coq 中实现的 :)
  • 无论您最终如何解决这个问题,您都需要一种方法来将来自xplusXFlags 与来自splusSFlags 结合起来。
  • @krokodil,也许你误解了单子。没有一个 monad 没有什么是你不能做的——它只是简写。所以打开包装,做你需要做的,然后再把它包起来。如果您的 monad 正在跟踪计算树,请不要在展开/包装时丢弃该信息。
  • 只使用状态单子?

标签: haskell monads monad-transformers writer-monad


【解决方案1】:

你可以使用mapWriter得到一些合理的东西。

sFromX :: XInt -> SInt
sFromX = mapWriter (\(x, XF fs) -> (x, SF fs))

现在你可以写foolike

foo :: SInt
foo = splus (return 1) (sFromX (xplus (return 2) (return 3)))

您可能也需要相反的xFromS。如果你有两个以上不同的幺半群,也许值得花点时间为标志容器编写一个类,比如:

class FlagContainer a where
  getFlags :: a -> Flags
  makeFromFlags :: Flags -> a

然后用它编写一个函数来替换sFromXxFromS和 你需要的任何其他人。 (虽然还没有测试过。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-26
    • 2014-01-16
    • 1970-01-01
    相关资源
    最近更新 更多