Writer monad 和无序
Writer monad and unsequence
我正在使用 Writer
monad 来跟踪任意值(例如 Int
)上的错误 ("collision") 标志。一旦设置了标志,它就是 "sticky" 并将其自身附加到作为标记的任何操作的结果产生的所有值。
有时碰撞标志与单个值相关联,有时我想与列表等复合结构相关联。当然,一旦为整个列表设置了冲突标志,就可以假设它是为单个元素设置的。所以对于 writer monad m
我需要以下两个操作:
sequence :: [m a] -> m [a]
unsequence :: m [a] -> [m a]
第一个在Prelude中定义,而第二个必须要定义。 Here 很好地讨论了如何使用 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>
鉴于此我有2个问题:
- 是否可以使用 monadic 和 comonadic 运算符定义
unsequence
,而不是特定于 Writer
?
- 上面的
extend
函数有没有更优雅的实现,或许类似于this one?
谢谢!
The ex1
produces correct value, while ex2
output is incorrectly not preserving collision flag:
unsequence
(因此,ex2
)不起作用,因为它会丢弃 Writer
日志。
unsequence :: (Comonad w, Monad m) => w [a] -> [m a]
unsequence = map return . extract
extract
for your Comonad
instance 给出计算结果,丢弃日志。 return
将 mempty
日志添加到裸结果中。既然如此,标志在 ex2
.
中被清除
unsequence1
,另一方面,做你想做的。这显然与 Comonad
没有任何关系(您的定义未使用其方法);相反,unsequence1
有效是因为...它实际上是 sequence
!在幕后,Writer
只是一对结果和一个(单向)日志。考虑到这一点,如果您再次查看 unsequence1
,您会注意到(模无关细节)它与 sequence
对成对的作用基本相同——它注释了另一个函子中的值与日志:
GHCi> sequence (3, [1..10])
[(3,1),(3,2),(3,3),(3,4),(3,5),(3,6),(3,7),(3,8),(3,9),(3,10)]
事实上,Writer
已经有一个Traversable
实例,所以你甚至不需要定义它:
GHCi> import Control.Monad.Writer
GHCi> import Data.Monoid -- 'Any' is your 'Bool' monoid.
GHCi> el = tell (Any True) >> return [1,2,3] :: Writer Any [Int]
GHCi> sequence el
[WriterT (Identity (1,Any {getAny = True})),WriterT (Identity (2,Any {getAny = True})),WriterT (Identity (3,Any {getAny = True}))]
值得一提的是,sequence
本质上并不是一个单子操作——sequence
中的 Monad
约束是不必要的限制。真正的交易是 sequenceA
, which only requires an Applicative
constraint on the inner functor. (If the outer Functor
-- i.e. the one with the Traversable
instance -- is like Writer w
in that it always "holds" exactly one value, then you don't even need Applicative
, but .)
Is it possible to define 'unsequence' using monadic and comonadic operators, not specific to 'Writer'
如上所述,您实际上并不想要 unsequence
。有一个叫做Distributive
的class确实提供了unsequence
(在distribute
的名字下);但是,具有 Distributive
个实例的事物与具有 Traversable
个实例的事物之间的重叠相对较少,并且在任何情况下它本质上都不涉及 comonads。
Is there is a more elegant implementatoin of extend function above, perhaps similar to this one?
您的 Comonad
实例很好(它确实遵循 the comonad laws), except that you don't actually need the Monoid
constraint in it. The pair comonad is usually known as Env
; see 来讨论它的作用。
我正在使用 Writer
monad 来跟踪任意值(例如 Int
)上的错误 ("collision") 标志。一旦设置了标志,它就是 "sticky" 并将其自身附加到作为标记的任何操作的结果产生的所有值。
有时碰撞标志与单个值相关联,有时我想与列表等复合结构相关联。当然,一旦为整个列表设置了冲突标志,就可以假设它是为单个元素设置的。所以对于 writer monad m
我需要以下两个操作:
sequence :: [m a] -> m [a]
unsequence :: m [a] -> [m a]
第一个在Prelude中定义,而第二个必须要定义。 Here 很好地讨论了如何使用 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>
鉴于此我有2个问题:
- 是否可以使用 monadic 和 comonadic 运算符定义
unsequence
,而不是特定于Writer
? - 上面的
extend
函数有没有更优雅的实现,或许类似于this one?
谢谢!
The
ex1
produces correct value, whileex2
output is incorrectly not preserving collision flag:
unsequence
(因此,ex2
)不起作用,因为它会丢弃 Writer
日志。
unsequence :: (Comonad w, Monad m) => w [a] -> [m a]
unsequence = map return . extract
extract
for your Comonad
instance 给出计算结果,丢弃日志。 return
将 mempty
日志添加到裸结果中。既然如此,标志在 ex2
.
unsequence1
,另一方面,做你想做的。这显然与 Comonad
没有任何关系(您的定义未使用其方法);相反,unsequence1
有效是因为...它实际上是 sequence
!在幕后,Writer
只是一对结果和一个(单向)日志。考虑到这一点,如果您再次查看 unsequence1
,您会注意到(模无关细节)它与 sequence
对成对的作用基本相同——它注释了另一个函子中的值与日志:
GHCi> sequence (3, [1..10])
[(3,1),(3,2),(3,3),(3,4),(3,5),(3,6),(3,7),(3,8),(3,9),(3,10)]
事实上,Writer
已经有一个Traversable
实例,所以你甚至不需要定义它:
GHCi> import Control.Monad.Writer
GHCi> import Data.Monoid -- 'Any' is your 'Bool' monoid.
GHCi> el = tell (Any True) >> return [1,2,3] :: Writer Any [Int]
GHCi> sequence el
[WriterT (Identity (1,Any {getAny = True})),WriterT (Identity (2,Any {getAny = True})),WriterT (Identity (3,Any {getAny = True}))]
值得一提的是,sequence
本质上并不是一个单子操作——sequence
中的 Monad
约束是不必要的限制。真正的交易是 sequenceA
, which only requires an Applicative
constraint on the inner functor. (If the outer Functor
-- i.e. the one with the Traversable
instance -- is like Writer w
in that it always "holds" exactly one value, then you don't even need Applicative
, but
Is it possible to define 'unsequence' using monadic and comonadic operators, not specific to 'Writer'
如上所述,您实际上并不想要 unsequence
。有一个叫做Distributive
的class确实提供了unsequence
(在distribute
的名字下);但是,具有 Distributive
个实例的事物与具有 Traversable
个实例的事物之间的重叠相对较少,并且在任何情况下它本质上都不涉及 comonads。
Is there is a more elegant implementatoin of extend function above, perhaps similar to this one?
您的 Comonad
实例很好(它确实遵循 the comonad laws), except that you don't actually need the Monoid
constraint in it. The pair comonad is usually known as Env
; see