在 Writer monad 中交换 `mappend`
Swapping `mappend` in Writer monad
总结:在使用 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,但我不确定它是否直接适用于此。我将不胜感激一些有关实现此类操作的最佳方法的指导。
你可以使用 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,比如:
class FlagContainer a where
getFlags :: a -> Flags
makeFromFlags :: Flags -> a
然后用它编写一个函数来替换 sFromX
、xFromS
和
你需要的任何其他人。 (不过还没有测试过。)
总结:在使用 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,但我不确定它是否直接适用于此。我将不胜感激一些有关实现此类操作的最佳方法的指导。
你可以使用 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,比如:
class FlagContainer a where
getFlags :: a -> Flags
makeFromFlags :: Flags -> a
然后用它编写一个函数来替换 sFromX
、xFromS
和
你需要的任何其他人。 (不过还没有测试过。)