在Writer monad中交换`mappend`

时间:2017-04-19 01:59:46

标签: haskell monads monad-transformers writer-monad

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

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

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但我不确定它是否适用于此处。我将非常感谢有关实施此类内容的最佳方法的一些指导。

1 个答案:

答案 0 :(得分:2)

您可以使用mapWriter获得合理的信息。

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

现在你可以写foo喜欢

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

您可能还需要相反的xFromS。如果你有两个以上不同的幺半群可能值得更好地为旗帜容器写一个类,如:

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

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