为什么在定义函数重复项时
duplicate :: w a -> w (w a)
对于Comonad类型类(link),您必须“在上下文中”修改所有元素(即,更改上下文的当前值以外的元素)。为什么不只是在Monad中使用return之类的东西呢?
示例(拉链):
data Z a = Z [a] a [a]
为什么我不能只将重复定义为
duplicate z = Z [] z []
我试图从Comonad规则中得出对重复函数的要求,但是我总是最终得到一个重复,该重复只是将元素包装起来就像在monad中返回一样,而无需执行任何其他操作。
一个blog post说:
重复项很难掌握。从列表拉链中,我们必须构建列表拉链的列表拉链。其背后的含义(由每个实例必须履行的共同法则确认)是,在重复结构内部移动将返回原始结构,并通过相同的移动进行更改
但是我不明白为什么一定要那样。 Comonad规则中的fmap始终适用于包装的元素,然后此元素总是与extract进行“包装”,为什么要麻烦在重复功能中做其他事情,而不仅仅是包装重复的参数?
你能指出我错过了什么吗?我觉得自己在某个地方犯了一些明显的错误,但我自己无法解决。
预先感谢您的答复!
最佳答案
重要的是,您可以对类型进行其他操作,而不只是简单地从其中输入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
d时,我们丢失了有关结构的信息。实际上,除了拉链的单个焦点之外,我们丢失了所有其他地方的信息。正确的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
相同的列表此外,我们可以看到左侧和右侧列表中的中间值必须与这些列表中的原始值相同。仅考虑这一定律,我们就足以知道
duplicate
的结果需要不同的结构。关于haskell - Comonad复制功能,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/27280941/