【问题标题】:If `zip` were a method of a lawful type class, of which then?如果 `zip` 是合法类型类的方法,那么是哪一个呢?
【发布时间】:2023-04-09 06:42:01
【问题描述】:

为什么会问这个问题 有人可能会说zipApplicative 的一种方法,通常的实例是ZipList。我对此不满意,因为它不安全。我也对Align 不满意,因为它包罗万象,过于复杂,而且对于一般情况来说不够具体。

合法类:Haskell 中的某些类型类可能被称为合法。这意味着它们带有必须遵守的平等——一个阶级的法律。这些规律通常来自编程的一个方面的范畴论概念化。例如,Monad计算 (无论是什么意思)通过同名的范畴理论设备的概念化。

重叠事物 想要对成箱的事物进行的通常操作是将它们放在彼此之上,如果它们是幺半群,它们就会融合。

例子:

没有足够的规律 这个概念的概念化是通过幺半群函子和相应的Applicative 类型类。然而,有一个烦人的复杂情况,因为通常有两种方法来定义Applicative,这两种方法看起来都很合适。为什么这样?我建议答案是“法律不够”

例子:

  • 算术:

    • Sum monoid 是实际的“endo-monoid”。它只对亲属事物是合法的。例如,您无法将质量和力相加。
    • Product 幺半群将维数 ab 转换为维数 c。增加质量和力量是合法的,可以让我们感到温暖。

    因此,可以从类型推断出正确的幺半群选择。

  • 对于列表:

    • 通常的列表direct sum 是更安全的列表。它可以简单地处理任何有限数量的元素,并且可以使用 “对角过程” 定义(例如 LogicT)处理其余有限数量。
    • ZipList 定义显然不安全。它被定义为,给定两个不同长度的列表,将较长的列表裁剪为较短的列表。
    • 长度索引向量是允许安全定义zip 的设备,它要求证明给定列表的长度相同。
  • 对于矩阵:

    • 通常的矩阵相加具有维度同质性的(非常合理)要求,与上面提到的长度索引向量相同。由于矩阵习惯性地用于各种现实世界的模拟,例如 3D 图形,一旦矩阵开始被裁剪或填充零,人们会立即抱怨,因此不会出现上述 ZipListZipMatrix 定义有吸引力。
    • 陌生人Kronecker multiplication 让人联想到列表的直接产物。它也承认Monad的定义。

两种情况 从这些例子中可以看出,我们称之为“monoid”“monoidal”的事物中混杂了两种不同的想法functor",而这种区别对于编程来说非常很重要(可能不像纯理论),因为它可以消除混乱,消除不安全因素,主要是,因为在每种情况下,要运行两个完全不相关的算法


我在想,也许单曲面函子的可逆性(也称为“强度”很重要。但是对 Peano 自然数的 Sum 和 Product 单曲面运算的结果是无法区分的。 (我不确定它们是否可以被认为是幺半群内函数。) 所以,我转而猜测类型的变化是标志。物理量的乘法不会像 Monoid 那样进行类型检查,甚至!

P.S. 有一个 Monad 的实例,用于笛卡尔积上的长度索引向量和 Kronecker 乘法上的矩阵,其中某种 fold zipjoin

【问题讨论】:

  • 可逆是什么意思?显而易见的定义类似于“如果liftA2 (,) xs ys == liftA2 (,) xs' ys',则xs == xs'ys == ys'”。但是通常的Applicative 列表实例(我认为你称之为“直接和”),你声称它是可逆的,不满足这个定义(例如liftA2 (,) [] [()] == liftA2 (,) [()] []),所以我不清楚究竟是什么你想在这里。 (这并不是对空虚的狡辩:也有非空列表破坏了可逆性。)
  • "有人可能会说zipApplicative的一个方法"。为什么?并且 all monids 是“endo-monoids”;定义以单一类型和对该类型的封闭二元运算开始。
  • @chepner 好吧,也许必须扩展通常的Hask 对“monoid”的理解。例如,长度索引向量是一个明显的幺半群​​,只是mappend 的定义中涉及到一个类型级别的(也是幺半群) 操作。另请注意,monad 是内函子类别中的一个幺半群。
  • @chepner 因为zipControl.Appicative.ZipList 中是liftA2 (,)。另外,因为Applicative 实际上在某种程度上代表“单形函子”,而definition thereof 会因为它与zip 的类型相似而让你震惊。
  • 在考虑向量和矩阵时,可能需要从二元运算中进行运算,而不是您所考虑的。除了笛卡尔积类型 ((a -> b -> c) -> v n a -> v m b -> v (n * m) c) 和 zip 类型 ((a -> b -> c) -> v n a -> v n b -> v n c),矩阵还有一种收缩类型:Monoid c => (a -> b -> c) -> m x y a -> m y z b -> m x z c `。也就是说,我认为合适的地方是形状索引的应用程序。这适用于明显可压缩且大小易于表达的向量。它也适用于函数。它不适用于地图。

标签: haskell typeclass


【解决方案1】:

精确的压缩(正如safe 包所称)可以通过the Representable class 表示。有很多与Representable 相关的理论。就我们目前的目的而言,我们可以专注于...

如果tabulateindex(->) x 同构,则Functor fRepresentable

...和:

Haskell 类型类别上的可表示内函子与 reader monad 同构,因此可以免费继承大量属性。

由于Representable 函子与某种类型的函数同构(例如,同构对与Bool -> a 同构,无限流与Nat -> a 同构),因此可以通过逐点压缩函数来实现精确压缩。这就是mzipRepMonadZipmzip 的默认实现)所做的:

mzipRep :: Representable f => f a -> f b -> f (a, b)
mzipRep as bs = tabulate (index as &&& index bs)

虽然 MonadZip is a rather awkward class(它主要是实现 MonadComprehensions 扩展的一部分),但它有一个相关的法律,我将用非单子的术语重申它:

信息保存:if () <$ u = () <$ v then munzip (mzip u v) = (u, v)

换句话说,如果uv 具有相同的形状,那么mzip 不会丢弃信息(因此它可以被munzip 撤消)。由于Representable 暗示只有一种可能的形状,它允许我们放弃条件,从而获得精确的拉链。


切线注释:

ZipList 定义显然是不安全的。它被定义为,给定两个不同长度的列表,将较长的列表裁剪为较短的列表。

我会说这取决于您要使用压缩的目的。有时你会想要或需要精确的压缩,有时你不会(例如,考虑使用zip [0..] 将索引附加到列表的常见技巧);有时填充而不是修剪是有意义的(参见leftaroundabout's comment)。这就是为什么我更喜欢将精确压缩称为“精确”,而不是“安全”。

然而,有一个令人讨厌的复杂情况,因为通常有两种方法来定义Applicative,这两种方法看起来都很合适。为什么这样?我建议答案是“法律不够”

我非常不同意如果一个类允许某个数据类型有多个实例,则该类未指定。我宁愿这样说,例如具有笛卡尔积应用程序的列表和具有压缩应用程序的列表是不同的结构,其特征在于相关的态射——碰巧它们可以通过相同的数据类型在 Haskell 中表示。

【讨论】:

  • 按照你的说法,对于一个有两个元素(Bool -> a),或者无限个元素(Natural -> a),但我想知道instance Representable (Vector (n :: Nat)) 对应于哪种类型的函数(其中VectorNat 类似于我在nearby question 中概述的那些) 。我们在 Haskell 中是否有合适的类型 (例如,FinOrd (n :: Nat)?..)
  • @IgnatInsarov 这确实是可行的。参照。例如finite-typelits,它又可以用于实现固定大小的向量with an exact zip,其风格类似于Dan Robertson 建议的in a comment here
猜你喜欢
  • 2011-04-26
  • 1970-01-01
  • 2021-02-26
  • 2012-08-19
  • 2020-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多