繁体   English   中英

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

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

我最近有点“将一切都提炼成它的基础”,我一直无法找到明确的理论原因来解释 Traversable 类型类是如何定义的,只有实际的“能够遍历是有用的”在应用性余数上,很多数据类型都可以做到”和很多提示。

我知道有一个适用的“家庭”,如https://duplode.github.io/posts/divisible-and-the-monoidal-quartet.html 所述

我还知道虽然 Traversable 遍历是应用煤代数,但“semigroupoids”中的 Traversable1 类型类描述了应用煤代数,而“distributive”中的 Distributive 类型类描述了函子代数。

此外,我知道 Foldable、Foldable1 和理论折叠家族成员描述了可以使用幺半群、半群和相应的幺半群家族成员折叠的数据类型,例如岩浆(用于折叠为二叉树)和每个的交换版本(用于折叠为每个的无序版本)。

因此,由于 Traversable 是 Foldable 的子类,我假设它本质上是幺半群的,同样我假设 Traversable1 本质上是半群的,而 Distributive 本质上是共半群的(如“分布式”包中的描述所述)。

这感觉是正确的轨道,但是 Applicative 和 Apply 从何而来? 有岩浆和交换版本吗? 在具有非平凡类群的类别中是否存在分配族?

本质上,我的问题是“这些类型类是否存在,它们是什么?如果不存在,为什么不存在?”:

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?

大概不太重要的奖金问题:在尝试研究这个时,我遇到了'data-functor-logistic' package https://hackage.haskell.org/package/data-functor-logistic

这描述了 Distributive over contravariant functors 的一个版本——是否有一个等效的 Traversable over Divisibles(或 Decidables)?

我不知道有任何库实现了这些类,但我会尝试阐明这些类代表什么。 我是一名程序员,而不是类别理论家,所以对此持保留态度。

Applicative变体

ApplyMagma

ApplyMagma class 与Apply class 具有完全相同的方法,但它不需要遵循结合律。

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

如果Apply类似于半群, ApplyMagma类似于岩浆.

ApplyCommute

ApplyCommute class 将等同于 Apply class,但具有以下交换律:

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

如果Apply类似于半群, ApplyCommute类似于交换半群。

Traversable1变体

Traversable1Magma

Traversable1Magma可以看作是Traversable1 ,提供了有关结构的更多信息。 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

可以将Traversable1Commute视为没有定义元素顺序的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))

请注意,这些是Traversable1的变体,因为ApplyMagmaApplyCommute都没有等同于pure的 function。

ContraTraversable

ContraTraversable没有任何实例。 要了解原因,请查看contraTraverse function 的类型。

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

我们可以将其专门用于以下方面:

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

这等效于以下内容:

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

使用const和来自 Divisible 的conquer function,这允许我们创建任何类型的值,这是不可能的。

自从问了这个问题并收到了上一个(优秀的)答案后,我了解到了使用 Applicative 的另一个原因:代数数据类型!

没有任何约束,它只能描述无人居住的数据类型,就像这样:

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

如果它是 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

本质上,它是具有任意数量(可能是无限的,如果在 Haskell 中可以描述的话)单个 FTraversable 参数(其中a ~ Identity a )的构造函数的任何数据类型。 这就是说任何 Traversable fa都同构于(f (), a) ,即 Writer 函子。

引入 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

现在构造函数可以有任意多个 arguments,只要它是大于零的有限数! 现在同构为(f (), NonEmpty a) ,它们的大小相等。

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

现在允许空构造函数! 现在同构为(f (), [a])

一个假设的可交换 Applicative 将用于可交换代数数据类型,如果它们是一回事——也许,如果 Set 强制它只能与可交换幺半群折叠,它们将是相关的,但据我所知,可交换数据类型不是 Haskell 的核心部分. 所以这种形式的遍历不会出现在代数数据类型中。

分布式是类似的,它用任意多条记录(可能是无限的)的单个构造函数来描述函子。

据我所知,Logistic 与这种代数解释无关 - 由于 Reader 函子通常与 Distributives 一起使用来创建 getter 函数的集合,因此 Logistic 旨在与 Op 逆变函子一起创建 setter 函数的集合。

这向我表明,Traversable 的等价物不存在,因为表征 Traversables 的 Writer 仿函数是它自己的对立面( (r,a)(a,r)同构)。

暂无
暂无

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

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