我正在使用Writer
monad 来跟踪任意值(例如Int
)上的错误(“冲突”)标志。一旦设置了标志,它就是“粘性的”,并将其自身附加到作为标记的任何操作的结果产生的所有值上。
有时碰撞标志与单个值相关联,有时我想与组合结构(例如列表)相关联。当然,一旦为整个列表设置了冲突标志,假设它是为单个元素设置的也是有意义的。因此,对于 writer monad m
,我需要以下两个操作:
sequence :: [m a] -> m [a]
unsequence :: m [a] -> [m a]
第一个在 Prelude 中定义,而第二个必须定义。这是关于如何使用comonads定义它的一个很好的讨论。本机comonad 实现不保留状态。这是一个例子:
{-# LANGUAGE FlexibleInstances #-}
module Foo where
import Control.Monad.Writer
import Control.Comonad
unsequence :: (Comonad w, Monad m) => w [a] -> [m a]
unsequence = map return . extract
instance Monoid Bool where
mempty = False
mappend = (||)
type CM = Writer Bool
type CInt = CM Int
instance (Monoid w) => Comonad (Writer w) where
extract x = fst $ runWriter x
extend f wa = do { tell $ execWriter wa ; return (f wa)}
mkCollision :: t -> Writer Bool t
mkCollision x = do (tell True) ; return x
unsequence1 :: CM [Int] -> [CInt]
unsequence1 a = let (l,f) = runWriter a in
map (\x -> do { tell f ; return x}) l
el = mkCollision [1,2,3]
ex2:: [CInt]
ex2 = unsequence el
ex1 = unsequence1 el
产生正确的ex1
值,而ex2
输出不正确地不保留碰撞标志:
*Foo> ex1
[WriterT (Identity (1,True)),WriterT (Identity (2,True)),WriterT (Identity (3,True))]
*Foo> ex2
[WriterT (Identity (1,False)),WriterT (Identity (2,False)),WriterT (Identity (3,False))]
*Foo>
鉴于此,我有两个问题:
- 是否可以
unsequence
使用不特定于 monadic 和 comonadic 运算符来定义Writer
? - 上面的函数是否有更优雅的实现
extend
,可能类似于这个?
谢谢!