了解为什么Zipper是一个Comonad
这是对我以前的问题的回答。
假设我需要将函数def f(a:A, leftNeighbors:List[A]): B
并且生成List[B]
每个项目a:A
List[A]
映射到b:B
.
显然,我不能只是在列表中调用map
,但我可以使用列表拉链 。 拉链是一个在列表中移动的游标。 它提供对当前元素( focus
)及其邻居的访问。
现在我可以用def f'(z:Zipper[A]):B = f(z.focus, z.left)
来代替我的f
,并将这个新函数f'
传递给Zipper[A]
cobind
方法。
cobind
工作原理是这样的:它用拉链调用f'
,然后移动拉链,用新的 “移动”拉链调用f'
,再次移动拉链等,依此类推…直到拉链到达名单末尾。
最后, cobind
返回一个types为Zipper[B]
的新拉链Zipper[B]
,它可以转换成列表,这样问题就解决了。
现在注意cobind[A](f:Zipper[A] => B):Zipper[B]
和bind[A](f:A => List[B]):List[B]
之间的对称性bind[A](f:A => List[B]):List[B]
Monad
和Zipper
是Comonad
。
是否有意义 ?
由于这个问题经常出现在“未答复”的名单上,请允许我在这里复制我的评论作为答案 – 无论如何,从一年前以来,没有什么比这更有build设性的了。
一个List
可以被视为一个comonad(以多种方式),而一个zipper可以被当作一个monad来使用(在很多方面也是如此)。 不同之处在于你是否在概念上专注于将数据“build设性地附加”到状态机(这就是Monad
接口的内容),或者“解构”地“解压缩”状态(这就是Comonad
所做的)。
但是,要回答这个问题并不容易,因为这个理解是有道理的。 从某种意义上说,它确实存在,而在另一个意义上则不然