为什么在定义函数重复
时 duplicate :: w a -> w (w a)
对于Comonad类型类(link),您必须修改上下文中的所有元素"" (即改变上下文的当前值以外的元素)。为什么不在Monad中使用 return 之类的东西?
示例(拉链):
data Z a = Z [a] a [a]
为什么我不能将副本定义为
duplicate z = Z [] z []
我试图从Comonad规则中获得对重复函数的要求,但我总是得到一个副本,它只是将元素包装成 return 一个monad,不需要做任何其他事情。
一个blog post说:
重复有点难以掌握。从列表拉链中,我们必须构建列表拉链的列表拉链。这背后的意义(由每个实例必须履行的comonad法律确认)是在复制结构内部移动返回原始结构,由同一移动改变
但我不明白为什么一定是那样。 Comonad规则中的 fmap 始终适用于包装元素,之后这一个元素始终是" unwrapped"使用提取,为什么还要在重复函数中做其他事情而不仅仅包装重复的参数?
你能说出我错过了什么吗?我觉得我在某个地方犯了一些明显的错误,但我无法自己搞清楚。
先谢谢你的回复!
答案 0 :(得分:6)
如果您可以使用该类型执行其他操作而不仅仅是extract
,那么这一点非常重要。直观地说,如果你唯一可以做的就是提取值,那么类型只保存一个值,所以复制那个值就是复制一切。这一般情况并非如此,拉链也不是这样。
Comonad
法律只是伪装w a -> b
类型函数的类别法。由于这些来自类别,因此根据类别而不是Comonad
法则来推理它们可能更容易。 extract
是此类别的标识,=<=
是合成运算符。
-- | Right-to-left 'Cokleisli' composition
(=<=) :: Comonad w => (w b -> c) -> (w a -> b) -> w a -> c
f =<= g = f . extend g
我们也知道extend f = fmap f . duplicate
,所以我们可以写
f =<= g = f . fmap g . duplicate
这看起来很容易理解。现在,让我们为Z
类型配备另一个我们可以谈论的功能。 isFirst
仅当Z
表示列表中某个位置的值时才会返回true。
isFirst :: Z a -> Bool
isFirst (Z [] _ _) = True
isFirst _ = False
现在,让我们考虑当我们将isFirst
与三个类别法律一起使用时会发生什么。似乎只有两个可以立即应用于extract
是由=<=
组成的左右标识。由于我们只反驳这一点,我们只需要找到一个反例。我怀疑extract =<= isFirst
或isFirst =<= extract
中的一个会因输入Z [1] 2 []
而失败。这两者都应与isFirst $ Z [1] 2 []
相同,即False
。我们首先尝试extract =<= isFirst
,这恰好可以解决。
extract =<= isFirst $ Z [1] 2 []
extract . fmap isFirst . duplicate $ Z [1] 2 []
extract . fmap isFirst $ Z [] (Z [1] 2 []) []
extract $ Z [] (isFirst (Z [1] 2 [])) []
extract $ Z [] False []
False
当我们尝试isFirst =<= extract
时,我们很幸运。
isFirst =<= extract $ Z [1] 2 []
isFirst . fmap extract . duplicate $ Z [1] 2 []
isFirst . fmap extract $ Z [] (Z [1] 2 []) []
isFirst $ Z [] (extract (Z [1] 2 [])) []
isFirst $ Z [] 2 []
True
当我们duplicate
时,我们失去了有关结构的信息。实际上,除了拉链的单个焦点外,我们丢失了有关到处传播的所有内容的信息。正确的duplicate
在上下文中的任何地方都会有一个完整的拉链,其中包含该位置的值和该位置的上下文。
让我们看看我们可以从这些法律中推断出什么。只需挥动一下功能类别,我们可以看到=<= extract
是fmap extract . duplicate
,这需要是身份功能。显然,我正在重新发现法律如何写在Control.Category
的文档中。这让我们写了像
z = (=<= extract) z
z = fmap extract . duplicate $ z
现在,z
只有一个构造函数,所以我们可以在
Z left x right = fmap extract . duplicate $ Z left x right
从它们的重复类型来看,我们知道它必须返回相同的构造函数。
Z left x right = fmap extract $ Z lefts (Z l x' r) rights
如果我们将fmap
应用于此Z
我们
Z left x right = Z (fmap extract lefts) (extract (Z l x' r)) (fmap extract rights)
如果我们通过Z
构造函数的部分将其拆分,我们有三个等式
left = fmap extract lefts
x = extract (Z l x' r)
right = fmap extract rights
这告诉我们至少duplicate (Z left x right)
的结果必须保持:
left
,Z
位于中间位置x
长度相同的列表此外,我们可以看到左侧和右侧列表中的中间值必须与这些列表中的原始值相同。考虑到这一法则,我们知道足以要求right
的结果采用不同的结构。