什么样的态射是类别理论中的“过滤器”?

时间:2018-05-30 01:58:47

标签: scala haskell functional-programming category-theory

在类别理论中,filter操作是否被视为态射?如果是,那是什么样的态射?示例(在Scala中)

val myNums: Seq[Int] = Seq(-1, 3, -4, 2)

myNums.filter(_ > 0)
// Seq[Int] = List(3, 2) // result = subset, same type

myNums.filter(_ > -99)
// Seq[Int] = List(-1, 3, -4, 2) // result = identical than original

myNums.filter(_ > 99)
// Seq[Int] = List() // result = empty, same type

4 个答案:

答案 0 :(得分:12)

在这个答案中,我假设您在filter上讨论Set(其他数据类型的情况似乎更加混乱)。

让我们先解决我们所说的问题。我将具体谈谈以下函数(在Scala中):

def filter[A](p: A => Boolean): Set[A] => Set[A] = 
                                     s => s filter p

当我们以这种方式写下来时,我们清楚地看到它是一个带有类型参数A的多态函数,它将谓词A => Boolean映射到将Set[A]映射到其他Set[A]的函数。 }。为了使它成为“态射”,我们必须首先找到一些类别,其中这个东西可能是“态射”。人们可能希望它是自然变换,因此“默认环境类别 - 结构”中的endofunctor类别中的态射通常被称为“Hask”(或“Scal”?“{ {1}}“?)。为了表明它是自然的,我们必须检查下面的图表是否为每个Scala通勤:

f: B => A
然而,在这里,我们立刻失败了,因为它不清楚甚至放在底部的水平箭头上,因为作业 - o f Hom[A, Boolean] ---------------------> Hom[B, Boolean] | | | | | | | filter[A] | filter[B] | | V ??? V Hom[Set[A], Set[A]] ---------------> Hom[Set[B], Set[B]] 甚至看起来都不是很有趣(出于同样的原因{{1} }不是函数,请参阅herehere)。

我在这里看到的修复类型A -> Hom[Set[A], Set[A]]的唯一“分类”结构如下:

  • A -> End[A]上的谓词可以被视为具有暗示的部分有序集合,A如果A暗示p LEQ q(即p必须q是假的,或p(x)必须对所有q(x)}都是真的。
  • 类似地,在函数x: A上,我们可以定义一个包含Set[A] => Set[A]的部分订单,每个集合f LEQ g保留s: Set[A]f(s)的子集

然后g(s)将是单调的,因此是poset类别之间的函子。但那有点无聊。

当然,对于每个固定的filter[A],它(或者更确切地说是它的eta扩展)也只是从AA => Boolean的函数,所以它自动成为“态射” “Set[A] => Set[A] - 类别”。但那更无聊。

答案 1 :(得分:10)

查看此事的一个有趣方式涉及不选择filter作为原始概念。有一个名为Filterable的Haskell类型类is aptly described as

  

Functor类似,但[包含] Maybe效果。

     

正式地,班级Filterable代表从 Kleisli可能 Hask 的仿函数。

"仿函数从Kleisli Maybe Hask "的态射映射由类的mapMaybe方法捕获,这确实是同名Data.Maybe函数的推广:

mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b

“阶级法”只是适当的仿函数法(请注意Just(<=<)分别是 Kleisli中的身份和构成>:

mapMaybe Just = id
mapMaybe (g <=< f) = mapMaybe g . mapMaybe f

该课程也可以用catMaybes ...

表示
catMaybes :: Filterable f => f (Maybe a) -> f a

...与mapMaybe可以相互确定(参见sequenceAtraverse之间的类似关系)...

catMaybes = mapMaybe id
mapMaybe g = catMaybes . fmap g

...相当于 Hask endofunctors Compose f Maybef之间的自然转换。

所有这些与你的问题有什么关系?首先,仿函数是类别之间的态射,自然变换是仿函数之间的态射。既然如此,就可以在less boring than the "morphisms in Hask" one的意义上谈论态射。你不一定想要这样做,但无论如何它都是现有的有利位置。

其次,filter也是Filterable的方法,其默认定义是:

filter :: Filterable f => (a -> Bool) -> f a -> f a
filter p = mapMaybe $ \a -> if p a then Just a else Nothing

或者,使用another cute combinator拼写它:

filter p = mapMaybe (ensure p)

在这个特定的分类概念中间接地给filter一个位置。

答案 2 :(得分:7)

filter可以用foldRight编写为:

filter p ys = foldRight(nil)( (x, xs) => if (p(x)) x::xs else xs ) ys
列表上的

foldRight是T-algebras的映射(这里T是List数据类型仿函数),因此filter是T-algebras的映射。

这里讨论的两个代数是初始列表代数

[nil, cons]: 1 + A x List(A) ----> List(A)

并且,让我们说&#34;过滤器&#34;代数,

[nil, f]: 1 + A x List(A) ----> List(A)

其中f(x, xs) = if p(x) x::xs else xs

在这种情况下,让filter(p, _)从初始代数到过滤器代数的唯一映射(在一般情况下称为fold)。它是代数映射的事实意味着满足以下等式:

filter(p, nil) = nil
filter(p, x::xs) = f(x, filter(p, xs))

答案 3 :(得分:6)

要回答这样的问题,我想首先了解过滤的本质是什么。

例如,输入是一个列表是否重要?你能过滤一棵树吗?我不明白为什么不!您将谓词应用于树的每个节点,并丢弃未通过测试的节点。

但结果的形状是什么?节点删除并不总是被定义或者它是模糊的。你可以返回一个清单。但为什么要列出?任何支持追加的数据结构都可行。您还需要数据结构的空成员来启动附加过程。所以任何单位岩浆都会这样做。如果你坚持相关性,你会得到一个幺半群。回顾filter的定义,结果是一个列表,这确实是一个幺半群。所以我们走在正确的轨道上。

所以filter只是一个特殊情况,称为Foldable:一种数据结构,您可以在将结果累积到一个幺半群时折叠。特别是,您可以使用谓词输出单例列表,如果它是真的;或者是一个空列表(标识元素),如果它是假的。

如果你想要一个绝对的答案,那么折叠就是一个变形现象的例子,这是代数范畴中的态射的一个例子。您正在折叠的(递归)数据结构(列表,在filter的情况下)是某个仿函数的初始代数(在本例中为列表仿函数),并且您的谓词用于为这个仿函数定义一个代数。