作家monad和unsequence [英] Writer monad and unsequence

查看:186
本文介绍了作家monad和unsequence的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Writer monad来跟踪任意值(例如 Int )。一旦标志被设置,它就是粘性,并将其自身附加到由于任何标记的操作而产生的所有值。

有时碰撞标志与个人价值观,有时我想与列表等复合结构相关联。当然,一旦碰撞标志被设置为整个列表,假定它被设置为单个元素也是有意义的。所以对于作者monad m 我需要以下两个操作:

  sequence :: [ma]  - > 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 =地图返回。提取

实例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

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>

鉴于此,我有两个问题:


  1. 是否可以使用monadic和comonadic操作符来定义 unsequence ,而不是特定于 Writer

  2. 上面的 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 做你想做的。这显然与 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}) )]

值得一提的是,序列本质上不是一次性操作 - sequence 中的 Monad 约束是不必要的限制。真正的交易是 sequenceA ,它只需要在内部仿函数上应用 Applicative 约束。 (如果外部的 Functor - 也就是具有 Traversable 实例的那个 - 就像 Writer w 因为它总是持有一个值,那么你甚至不需要 Applicative ,但是


是否可以定义' unsequence'使用monadic和comonadic操作符,而不是'Writer'特有的

如上所述,您实际上不需要 unsequence 。有一个类叫 分配 ,它提供 unsequence (在 distribute 的名称下);然而,在 Distributive 实例和 Traversable 之间的事物之间相对较少重叠,并且在任何情况下它都不会基本上涉及comonads。


上面的扩展函数是否有更优雅的实现,或许与此类似?


您的 Comonad 实例很好(它遵循 comonad laws ),除非您实际上并不需要 Monoid 约束。这对组合通常被称为 的Env ;请参阅这个答案,了解它的功能。

I am using the 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.

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 m I need the two following operations:

sequence :: [m a] -> m [a]
unsequence :: m [a] -> [m a]

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:

{-# 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

The 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> 

In view of this I have 2 questions:

  1. Is it possible to define unsequence using monadic and comonadic operators, not specific to Writer?
  2. Is there is a more elegant implementation of the extend function above, perhaps similar to this one?

Thanks!

解决方案

The 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)]

In fact, 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}))]

It is worth mentioning that 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.)

Is it possible to define 'unsequence' using monadic and comonadic operators, not specific to 'Writer'

As discussed above, you don't actually want 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.

Is there is a more elegant implementatoin of extend function above, perhaps similar to this one?

Your 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.

这篇关于作家monad和unsequence的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆