作家monad和unsequence [英] Writer monad and unsequence
问题描述
我使用 有时碰撞标志与个人价值观,有时我想与列表等复合结构相关联。当然,一旦碰撞标志被设置为整个列表,假定它被设置为单个元素也是有意义的。所以对于作者monad 第一个在Prelude中定义,第二个必须定义。 这里是一个很好的讨论,它是如何使用 comonads 。本地comonad实现不保留状态。这是一个例子: Writer
monad来跟踪任意值(例如 Int $)上的错误(碰撞)标志C $ C>)。一旦标志被设置,它就是粘性,并将其自身附加到由于任何标记的操作而产生的所有值。
会产生正确的值,而
m
我需要以下两个操作:
sequence :: [ma] - > m [a]
unsequence :: m [a] - > [m a]
{ - #LANGUAGE FlexibleInstances# - }
module Foo where
import Control.Monad.Writer
import Control.Comonad
unsequence ::(Comonad w,Monad m)=> w [a] - > [m a]
unsequence =地图返回。提取
实例Monoid Bool其中
mempty = False
mappend =(||)
类型CM = Writer Bool
类型CInt = CM Int
instance(Monoid w)=> Comonad(Writer w)其中
提取x = fst $ runWriter x
扩展f wa = do {tell $ execWriter wa; return(f wa)}
mkCollision :: t - > Writer Bool t
mkCollision x = do(tell True);返回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 =不确定e
ex1 =不相序1 el
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>
鉴于此,我有两个问题:
- 是否可以使用monadic和comonadic操作符来定义
unsequence
,而不是特定于Writer
? - 上面的
extend
函数是否有更优雅的实现,可能类似于 this one ?
谢谢!
<ex1
会产生正确的值,而ex2
输出错误地不会保留碰撞标志:
unsequence
(因此, ex2
)不起作用,因为它抛出 Writer
log。
unsequence ::(Comonad w,Monad m)=> w [a] - > [m a]
unsequence =地图返回。为您的<$ c $提取
摘录
c> Comonad 实例给出计算结果,放弃日志。 return
将一个 mempty
日志添加到裸露的结果中。如此,标记在 ex2
中清除。
unsequence1 $ c另一方面,$ c>做你想做的。这显然与
Comonad
没有任何关系(您的定义不使用它的方法);相反, unsequence1
可以工作,因为它实际上是 sequence
!底下, Writer
只是一对结果和一个(monoidal)日志。如果再仔细看看 unsequence1
,你会注意到(模不相关的细节)它和序列基本相同
对 - 它用日志注释其他函数中的值:
GHCi>序列(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'是你的'Bool'monoid。
GHCi> el = tell(Any True)>> return [1,2,3] :: Writer任何[Int]
GHCi>序列el
[WriterT(Identity(1,Any {getAny = True})),WriterT(Identity(2,Any {getAny = True})),WriterT(Identity(3,Any {getAny = True}) )]
值得一提的是, 是否可以定义' unsequence'使用monadic和comonadic操作符,而不是'Writer'特有的 如上所述,您实际上不需要 上面的扩展函数是否有更优雅的实现,或许与此类似? 您的 I am using the Sometimes the collision flag is associated with individual values, sometimes I would like to associate with composite structures such as lists. Of course, once the collision flag is set for a whole list, it also makes sense to assume it is set for an individual element. So for a writer monad The first one is defined in the Prelude, while the second one has to be defined. Here is a good discussion of how it could be defined using comonads. A native comonad implementation does not preserve the state. Here is an example: The In view of this I have 2 questions: Thanks! The In fact, It is worth mentioning that Is it possible to define 'unsequence' using monadic and comonadic operators, not specific to 'Writer' As discussed above, you don't actually want Is there is a more elegant implementatoin of extend function above, perhaps similar to this one? Your 这篇关于作家monad和unsequence的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!序列
本质上不是一次性操作 - sequence
中的 Monad
约束是不必要的限制。真正的交易是 sequenceA
,它只需要在内部仿函数上应用 Applicative
约束。 (如果外部的 Functor
- 也就是具有 Traversable
实例的那个 - 就像 Writer w
因为它总是持有一个值,那么你甚至不需要 Applicative
,但是
unsequence
。有一个类叫 分配
,它提供 unsequence
(在 distribute
的名称下);然而,在 Distributive
实例和 Traversable
之间的事物之间相对较少重叠,并且在任何情况下它都不会基本上涉及comonads。
Comonad
实例很好(它遵循 comonad laws ),除非您实际上并不需要 Monoid
约束。这对组合通常被称为 的Env
;请参阅这个答案,了解它的功能。 Writer
monad to keep track of an error ("collision") flag on arbitrary values (such as Int
). Once the flag is set it is "sticky" and attaches itself to all values produced as a result of any operation with the marked one.m
I need the two following operations:sequence :: [m a] -> m [a]
unsequence :: m [a] -> [m a]
{-# 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
produces the correct value, while ex2
output is incorrectly not preserving collision flag:*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
using monadic and comonadic operators, not specific to Writer
?extend
function above, perhaps similar to this one?
ex1
produces correct value, while ex2
output is incorrectly not preserving collision flag:unsequence
(and, as a consequence, ex2
) doesn't work because it throws away the Writer
log.unsequence :: (Comonad w, Monad m) => w [a] -> [m a]
unsequence = map return . extract
extract
for your Comonad
instance gives the result of the computation, discarding the log. return
adds a mempty
log to the bare results. That being so, the flags are cleared in ex2
.unsequence1
, on the other hand, does what you want. That clearly doesn't have anything to do with Comonad
(your definition doesn't use its methods); rather, unsequence1
works because... it's actually sequence
! Under the hood, Writer
is just a pair of a result and a (monoidal) log. If you have a second look at unsequence1
with that in mind, you will note that (modulo irrelevant details) it does essentially the same thing than sequence
for pairs -- it annotates the values in the other functor with the log: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
already has a Traversable
instance just like that, so you don't even need to define it: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
isn't an essentially monadic operation -- the Monad
constraint in sequence
is unnecessarily restrictive. The real deal is 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 that's another story.)
unsequence
. There is a class called Distributive
that does provide unsequence
(under the name of distribute
); however, there is relatively little overlap between things with Distributive
instances and things with Traversable
ones, and in any case it doesn't essentially involve comonads.
Comonad
instance is fine (it does follow the comonad laws), except that you don't actually need the Monoid
constraint in it. The pair comonad is usually known as Env
; see this answer for discussion of what it does.