在本演示文稿[2005] 中,我们在幻灯片 32 处阅读:
zipper 数据类型隐藏了一个comonad。这正是构建属性评估所需要的共同点。
因此,您似乎可以用 Comonads 来表达 Zippers。这在 Scala 中似乎是可能的。
查看zipper 源代码,我们看到 zippers 表示为 Clojure 元数据。
我的问题是,有什么证据表明 Clojure 拉链会从以共单胞形式表达中受益?
Eric 建议这样做的好处是
所以我们需要在原始组上获得所有可能的拉链!
在本演示文稿[2005] 中,我们在幻灯片 32 处阅读:
zipper 数据类型隐藏了一个comonad。这正是构建属性评估所需要的共同点。
因此,您似乎可以用 Comonads 来表达 Zippers。这在 Scala 中似乎是可能的。
查看zipper 源代码,我们看到 zippers 表示为 Clojure 元数据。
我的问题是,有什么证据表明 Clojure 拉链会从以共单胞形式表达中受益?
Eric 建议这样做的好处是
所以我们需要在原始组上获得所有可能的拉链!
您所问的内容存在一些结构性谬误。并不是说 Zippers可以表示为 Comonads,而是它们是天生的。
同样,无论您是否选择接受这个事实,整数都是幺半群(有两种方式!)。
所以,与其问有什么好处,你应该问的是“我可以通过识别单子结构来提高清晰度吗?”
答案是“是的!”
Comonadic 结构意味着在任何拉链上都存在两种有趣的方法。第一个是显而易见且明显有用的——“here”功能。为了使这更具体,我将制作一个列表拉链
data Zipper a = Zipper { before :: [a], here :: a, after :: [a] }
现在here :: Zipper a -> a
是通常称为 的 comonadic 函数extract
。
extract = here
因此,可以公平地说,每次您检查拉链指向的东西时,您都在使用 comonadic interface。
也就是说,extract
是界面无聊的一面。更有趣的是extend
。
extend :: (Zipper a -> b) -> Zipper a -> Zipper b
抓住的是extend
对拉链中的每个元素应用“上下文转换”的想法。Comonadic 结构指出,有一种标准且结构良好的方法可以通过“ extend
ing”到整个comonad 的转换来实现。
这样的例子可能是对列表应用卷积——例如,一个小的模糊函数:
blurKernel :: Fractional a => Zipper a -> a
blurKernel (Zipper prior current future) =
(a + current + c) / 3
where
a = case prior of
[] -> 0
(p:ps) -> p
c = case future of
[] -> 0
(p:ps) -> p
blur :: Fractional a => Zipper a -> Zipper a
blur = extend blurKernel
那么为什么要写blur
这些术语呢?有没有一种自然的、递归的或迭代的公式可以发挥同样的作用并且更明显?
好吧,通过认识到这blur
是基于一个comonadic 扩展,我们已经在我们对Zippers 的操作中暴露了共同的结构。这有利于保持 DRY。
我们也开始认识到 Zippers 的一些深刻的东西——每个 zipper 都有 comonadic extend
,所以也许我们可以通过某种方式只概括并在我们关心的每个 Zipper 中泛化它来概括blur
所有类型的 Zipper。Fractional
blurKernel
extend
无论如何,我希望我的例子能够证明无论你是否注意到,Zippers 都是共生词。
良好的 Haskell 抽象通常就是这种情况——它们是某些类型代码操作方式的自然属性。类型类只是为了方便而捕获它们。Maybe
// State
/ List
etc 将是 monad,即使它们不是Monad
s。和Zipper
/ Store
/Trace
即使它们不是Comonad
s 也会是共胞。