简体   繁体   English

从根本上说,为什么遍历是在应用程序上定义的?

[英]Why are traversals defined over Applicatives, fundamentally?

I've been on a bit of a "distilling everything to its fundamentals" kick lately, and I've been unable to find clear theoretical reasons for how the Traversable typeclass is defined, only practical ones of "it's useful to be able to traverse over applicative coalgebras, and lots of datatypes can do it" and a whole lot of hints.我最近有点“将一切都提炼成它的基础”,我一直无法找到明确的理论原因来解释 Traversable 类型类是如何定义的,只有实际的“能够遍历是有用的”在应用性余数上,很多数据类型都可以做到”和很多提示。

I'm aware that there's an applicative "family", as described by https://duplode.github.io/posts/divisible-and-the-monoidal-quartet.html .我知道有一个适用的“家庭”,如https://duplode.github.io/posts/divisible-and-the-monoidal-quartet.html 所述

I'm also aware that while Traversable traversals are applicative coalgebras, the Traversable1 typeclass from 'semigroupoids' describes apply coalgebras, and the Distributive typeclass from 'distributive' describes functor algebras.我还知道虽然 Traversable 遍历是应用煤代数,但“semigroupoids”中的 Traversable1 类型类描述了应用煤代数,而“distributive”中的 Distributive 类型类描述了函子代数。

Additionally, I'm aware that Foldable, Foldable1, and theoretical fold family members, describe datatypes that can be folded using monoids, semigroups, and corresponding monoid family members such as magmas (for folding as a binary tree) and commutative versions of each (for folding as unordered versions of each).此外,我知道 Foldable、Foldable1 和理论折叠家族成员描述了可以使用幺半群、半群和相应的幺半群家族成员折叠的数据类型,例如岩浆(用于折叠为二叉树)和每个的交换版本(用于折叠为每个的无序版本)。

As such, as Traversable is a subclass of Foldable, I assume it's monoidal in nature, and similarly I assume Traversable1 is semigroupal in nature, and Distributive is comonoidal in nature (as mentioned in its description in the 'distributive' package).因此,由于 Traversable 是 Foldable 的子类,我假设它本质上是幺半群的,同样我假设 Traversable1 本质上是半群的,而 Distributive 本质上是共半群的(如“分布式”包中的描述所述)。

This feels like the right track, but where do Applicative and Apply come from here?这感觉是正确的轨道,但是 Applicative 和 Apply 从何而来? Are there magmatic and commutative versions?有岩浆和交换版本吗? Would there be a distributive family in a category with non-trivial comonoids?在具有非平凡类群的类别中是否存在分配族?

Essentially, my question is "do these typeclasses exist, and what are they? if not, why not?":本质上,我的问题是“这些类型类是否存在,它们是什么?如果不存在,为什么不存在?”:

class FoldableMagma t => TraversableMagma t where
    traverseMagma :: ??? f => (a -> f b) -> (t a -> f (t b))
class FoldableCommute t => TraversableCommute t where
    traverseCommute :: ??? f => (a -> f b) -> (t a -> f (t b))
class Foldable t => ContraTraversable t where
    contraTraverse :: Divisible f => (b -> f a) -> (t a -> f (t b))
-- im really not sure on this last one
-- but it's how i'd expect an endofunctor over coalgebras to look
-- which seems potentially related to traversables?

Presumably less important bonus question: while attempting to research this, I came across the 'data-functor-logistic' package https://hackage.haskell.org/package/data-functor-logistic大概不太重要的奖金问题:在尝试研究这个时,我遇到了'data-functor-logistic' package https://hackage.haskell.org/package/data-functor-logistic

This describes a version of Distributive over contravariant functors - is there an equivalent Traversable over Divisibles (or Decidables)?这描述了 Distributive over contravariant functors 的一个版本——是否有一个等效的 Traversable over Divisibles(或 Decidables)?

I'm not aware of any library that implements those classes, but I'll try to unravel what those classes would represent.我不知道有任何库实现了这些类,但我会尝试阐明这些类代表什么。 I am a programmer, not a category theorist, so take this with a grain of salt.我是一名程序员,而不是类别理论家,所以对此持保留态度。

Applicative variants Applicative变体

ApplyMagma

The ApplyMagma class has exactly the same methods as the Apply class, but it doesn't need to follow the associativity law. ApplyMagma class 与Apply class 具有完全相同的方法,但它不需要遵循结合律。

class Functor f => ApplyMagma f where
    (<.>) :: f (a -> b) -> f a -> f b

If Apply is analogous to semigroups, ApplyMagma is analogous to magmas.如果Apply类似于半群, ApplyMagma类似于岩浆.

ApplyCommute

The ApplyCommute class will be equivalent to the Apply class but with the following commutativity law: ApplyCommute class 将等同于 Apply class,但具有以下交换律:

f <$> x <.> y = flip f <$> y <.> x

If Apply is analogous to semigroups, ApplyCommute is analogous to commutative semigroups.如果Apply类似于半群, ApplyCommute类似于交换半群。

Traversable1 variants Traversable1变体

Traversable1Magma

A Traversable1Magma can be seen as a Traversable1 with more information provided about the structure. Traversable1Magma可以看作是Traversable1 ,提供了有关结构的更多信息。 While the Foldable1 class has a toNonEmpty method, The Foldable1Magma class could have a toBinaryTree method. Foldable1 class 有一个toNonEmpty方法,而Foldable1Magma class 可以有一个toBinaryTree方法。

class (FoldableMagma t, Traversable1 t) => Traversable1Magma t where
    traverseMagma :: ApplyMagma f => (a -> f b) -> (t a -> f (t b))

Traversable1Commute

A Traversable1Commute can be seen as a Traversable1 without a defined ordering to the elements.可以将Traversable1Commute视为没有定义元素顺序的Traversable1 If it didn't require an Ord a constraint, Set from containers could be an instance of this class. Traversable1Commute could be a superclass of Traversable1.如果不需要Ord a约束, Set from containers可以是这个 class 的实例。Traversable1Commute 可以是 Traversable1 的超类。

class (FoldableCommute t, Functor t) => Traversable1Commute t where
    traverseCommute :: ApplyCommute f => (a -> f b) -> (t a -> f (t b))

Note that these are variants of Traversable1 because neither ApplyMagma nor ApplyCommute have a function equivalent to pure .请注意,这些是Traversable1的变体,因为ApplyMagmaApplyCommute都没有等同于pure的 function。

ContraTraversable

ContraTraversable does not have any instances. ContraTraversable没有任何实例。 To see why, look at the type of the contraTraverse function.要了解原因,请查看contraTraverse function 的类型。

contraTraverse :: Divisible f => (b -> f a) -> (t a -> f (t b))

We can specialize this to the following:我们可以将其专门用于以下方面:

contraTraverse :: Monoid b => (b -> Op b a) -> (t a -> Op b (t b))

Which is eqivalent to the following:这等效于以下内容:

contraTraverse ~ Monoid b => (b -> a -> b) -> t a -> t b -> a

Using const and the conquer function from Divisible, this allows us to create a value of any type, which is impossible.使用const和来自 Divisible 的conquer function,这允许我们创建任何类型的值,这是不可能的。

Since asking this and receiving the previous (excellent) answer, I have learnt another reason why Applicative is used: algebraic datatypes!自从问了这个问题并收到了上一个(优秀的)答案后,我了解到了使用 Applicative 的另一个原因:代数数据类型!

Without any constraint, it can only describe uninhabited datatypes, like this:没有任何约束,它只能描述无人居住的数据类型,就像这样:

data V1 a
instance VTraversable V1 where
    vtraverse _ = \case
-- uninhabited types can be pattern matched to create any result type

If it were Functor, it could describe algebraic datatypes that look something like these:如果它是 Functor,它可以描述如下所示的代数数据类型:

data FTrav1 a = FTrav1 a
instance FTraversable FTrav1 where
    ftraverse strat (FTrav1 a) = FTrav1 <$> strat a

data FTrav2 a = FTrav2_1 a | FTrav2_2 (FTrav1 a)
instance FTraversable FTrav2 where
    ftraverse strat (FTrav2_1 a) = FTrav2_1 <$> strat a
    ftraverse strat (FTrav2_2 fa) = FTrav2_2 <$> ftraverse strat fa

Essentially, it's any datatype with an arbitrary (potentially infinite, if that were describable in Haskell) number of constructors of a single FTraversable argument (where a ~ Identity a ).本质上,它是具有任意数量(可能是无限的,如果在 Haskell 中可以描述的话)单个 FTraversable 参数(其中a ~ Identity a )的构造函数的任何数据类型。 This is saying that any Traversable fa is isomorphic to (f (), a) , the Writer functor.这就是说任何 Traversable fa都同构于(f (), a) ,即 Writer 函子。

Introducing Apply enables extra datatypes like the following:引入 Apply 可以启用额外的数据类型,如下所示:

data ApplyTrav1 a = ApplyTrav1 a a
instance Traversable1 ApplyTrav1 where
    traverse1 strat (ApplyTrav1 a a) = ApplyTrav1 <$> strat a <*> strat a

data ApplyTrav2 a = ApplyTrav2_1 a (ApplyTrav1 a) | ApplyTrav2_2 (ApplyTrav1 a) a
instance Traversable1 ApplyTrav2 where
    traverse1 strat (ApplyTrav2_1 a fa) = ApplyTrav2_1 <$> strat a <*> traverse1 strat fa
    traverse1 strat (ApplyTrav2_2 fa a) = ApplyTrav2_2 <$> traverse1 strat fa <*> traverse1 strat a

Now constructors can have arbitrarily many arguments, as long as it's a finite number greater than zero!现在构造函数可以有任意多个 arguments,只要它是大于零的有限数! The isomorphism is now to (f (), NonEmpty a) , where they are of equal sizes.现在同构为(f (), NonEmpty a) ,它们的大小相等。

Applicative enables the following: Applicative 启用以下功能:

data ApplicTrav a = ApplicTrav0 | ApplicTrav a a
instance Traversable ApplicTrav where
    traverse _ ApplicTrav0 = pure ApplicTrav0
    traverse strat (ApplicTrav a a) = ApplicTrav <$> strat a <*> strat a

Now empty constructors are allowed!现在允许空构造函数! The isomorphism is now to (f (), [a]) .现在同构为(f (), [a])

A hypothetical commutative Applicative would be used for commutative algebraic datatypes, were they a thing - perhaps, if Set enforced that it were only foldable with commutative monoids, they would be relevant, But to my knowledge, commutative datatypes are not a core part of Haskell. and so this form of traversal will not show up for algebraic datatypes.一个假设的可交换 Applicative 将用于可交换代数数据类型,如果它们是一回事——也许,如果 Set 强制它只能与可交换幺半群折叠,它们将是相关的,但据我所知,可交换数据类型不是 Haskell 的核心部分. 所以这种形式的遍历不会出现在代数数据类型中。

Distributive is similar, it describes functors with a single constructor of arbitrarily many records (potentially infinite).分布式是类似的,它用任意多条记录(可能是无限的)的单个构造函数来描述函子。

Logistic is unrelated to this algebraic interpretation, to my knowledge - since the Reader functor is commonly used with Distributives to create a collection of getter functions, Logistic is designed to work with the Op contravariant functor to create a collection of setter functions.据我所知,Logistic 与这种代数解释无关 - 由于 Reader 函子通常与 Distributives 一起使用来创建 getter 函数的集合,因此 Logistic 旨在与 Op 逆变函子一起创建 setter 函数的集合。

This suggests to me that the equivalent for Traversable doesn't exist, due to the Writer functor that characterises Traversables being its own opposite ( (r,a) is isomorphic to (a,r) ).这向我表明,Traversable 的等价物不存在,因为表征 Traversables 的 Writer 仿函数是它自己的对立面( (r,a)(a,r)同构)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM