编程中是否有通用函子(不限于endofunctor)的用法?

Is there any general functor (not limited to endofunctor) usage in programming?

在编程中有没有通用函子(不限于endofunctor)的用法?

我理解使用 endofunctor 的原因是为了使结构像 monoid 或 monad 一样简单。

我也最终理解,所有的价值都归结为编程语言的一个类别(例如Hask),但我这里说的是同一类别之间的endofunctor Strings, Numbers, Booleans,或函数。

相关问题:

Are all Haskell functors endofunctors?

Differences between functors and endofunctors

首先,

比如我们都知道幺半群可以定义为single-object类

  • 箭头是元素
  • 单个对象没有意义
  • 要作为运算符的组合(Haskell 中的(<>)
  • id 箭头为标识(mempty in Haskell)。

在这个意义上,两个幺半群之间的同态成为两个范畴之间的函子。

现在,比方说,类型 AB 都是幺半群;它们之间的函子只是一个同态函数 f :: A -> B,它将每个 A 映射到 B,并保持组合。

But, wait, f :: A -> B is not even a Functor (note that I use the monospaced typeface here)!

不,它不是 Haskell 中的 Functor,但它仍然 数学意义上的函子。

所以,为了强调,我再次声明:"Non-endo" 函子在编程中使用,而且可能比内函子更频繁。

这里的重点是范畴论是一种高度抽象的理论——它提供了抽象具体对象的概念。我们可以将这些概念定义为在不同的上下文中表示不同的事物。

Hask(或Set,或Set的子类别)只是这些无限定义之一,这使得

  • 箭头成为函数
  • 对象类型(或集合)
  • 复合为函数复合(.)
  • id 箭头是 id 函数。

将此“分类宇宙”定义与上面的“分类幺半群”定义进行比较 - 恭喜,您现在已经了解了两种不同的类别!

总而言之,请记住范畴论本身只是一些抽象。抽象本身没有任何意义,也没有任何用处。我们将它们与真实的事物联系起来,只有这样它们才能给我们带来便利。通过具体的例子理解抽象概念,但永远不要将这些概念本身简化为任何具体的东西(比如,永远不要将函子简化为某个“分类宇宙”中的函子(例如 Hask设置,等等)!).

P.S. If you ask "Is there a functor that sends Hask to another category in Haskell?" then the answer can be yes or no. For example, you can define a category Hask * Hask to contain any two types' cartesian product, and a functor data Diag a = Diag a a, fmap f x = Diag (f x) (f x) that sends each type A to its square A * A. However, Hask * Hask is still a subcategory of Hask, so we may say this is an endofunctor too.

简短的回答:是的, Haskell 中有 'smaller' 个类别,您可以在它们之间定义函子(不仅仅是内函子)。它们是否有用是另一个问题。

这是我多年来一直想知道的事情。目前的问题促使我尝试一下。我目前正在第三次阅读 Bartosz Milewski 的 Category Theory for Programmers。我不确定我是否做对了以下内容,因此非常感谢您提供反馈。

哈斯克

如果我没理解错的话,Hask本质上就是types的范畴(~sets[=的范畴253=]) 与 bottom (⊥) 被抛出以表示 non-terminating 计算。这是对其进行说明的尝试:

Hask 中的每个 object 都是一个 type 就像 Int, BoolString 或您自己的自定义类型,如 ReservationOrder 等。一个类型可以看作是一个 set;例如Bool 是包含 TrueFalse 的集合,String 是所有字符串的集合,等等。显然,其中许多集合(如 String)是无限。

此外,还有特殊的底部对象

你可以将类型映射到其他类型,但你不能映射到 Hask 之外的东西,因为 Hask 包含所有类型并且表达式:

在这里,我通过复制 Hask 来说明从 HaskHask 的映射,但是实际上,这两个类别只是两个相同的图像。

函子是一种映射,不仅可以映射对象,还可以映射对象之间的态射。关于这个已经说了很多,所以我在这里唯一要说的是,因为 HaskHask 之间的仿函数不会离开类别,它们是函子 within Hask,因此是 endofunctors。那就是 Functor 类型 class in Haskell.

单位类别

那么问题是:'smaller' Hask 中是否有类别?

据我所知:是的,无限多。

存在的最简单范畴之一是具有单个对象且除恒等态射外没有其他态射的范畴:

在Haskell中,这可能是单位())类型的图片。虽然 ()Hask 的一部分,但您也可以将其视为一个类别。我们称它为单位.

免费类别

上面的单位类别只是free category的一个例子。自由类别是由有向图构造的类别。这是另一张图:

这个有两个顶点和两条边。我们可以通过将顶点解释为对象并将边解释为态射来从该图中构造一个类别。我们还必须为每个对象添加身份态射,以及态射的组合。

在编程中,一个有两个对象的集合等同于一个只有两个居民的类型。你可以给这些值取不同的名字,但这样的类型总是同构于 Bool.

函子

我们可以定义上述两个类别之间的映射吗?

是的,我们可以通过在 'larger' 类别中嵌入 Unit 来做到这一点。我们通过任意选择一个对象来做到这一点:

存在另一个拾取另一个对象的仿函数。

这显然是 类别之间的映射,因此不是内函子。不过,它是真函子吗?

要成为函子,映射不仅要将对象映射到对象,还要将态射映射到态射。这里也是如此,因为Unit只有恒等态射。因此,我们还将恒等态射映射到我们选择的目标对象上的恒等态射。 Unit 中唯一可能的组合是 id ∘ idid ∘ id ∘ id,依此类推。这些都映射到目标对象上的id ∘ idid ∘ id ∘ id等。

我只接触范畴论几年,但我认为这是一个真函子。

Haskell分类类型class

Haskell 定义了一个名为 Category 的类型 class。它不太适合上面的 Unit 类别,或者上面的自由类别示例,因为它假设 Category 是 higher-kinded 类型(即它涉及类型) 在 Hask 中。尽管如此,让我们看看是否可以将 Unit 和上面的自由类别塞进 Category,并从中创建一个函子。

单位Category

Category 的实例必须是 higher-kinded 类型(即 cat a b),所以我们不能只将 () 变成 Category 实例。但是,我们可以定义一个与它同构的 higher-kinded 类型:

data U a b = U deriving (Eq, Show)

Const 仿函数一样,这个类型定义了类型 var它然后忽略的能力。和()一样,U类型只有一个值,也叫U。 (练习:证明 U() 是同构的。)

我们可以让U成为一个Category实例:

instance Category U where
  id = U
  U . U = U

不过,这是一个合适的类别吗?它遵守法律吗?

我们可以用等式推理来证明:

正确身份

  U . id
= { definition of (.) }
  U

留下身份

  id . U
= { definition of (.) }
  U

关联性

  U . (U . U)
= { definition of (.) }
  U . U
= { redundant brackets }
  (U . U)
= { definition of (.) }
  (U . U) . U

我觉得不错。

免费类别示例为Category

上面的免费类别示例怎么样?像上面的 U 类型一样,这个微小的类别不能是参数多态的,但是我们可以再次定义一个幻像类型:

data Bendo a b = Bendo { runB :: Bool -> Bool }

other :: Bendo a b
other = Bendo not

我将 Bendo 类型称为 布尔自同态 ,因为事实证明它就是这样。两个对象(TrueFalse)之间的边对应于拾取other对象,相当于built-innot函数。

要对相关类别建模,唯一允许的态射是 otherid,因此其他函数 Bool -> Bool(如 \_ -> True)应该被禁止。因此,定义 Bendo 的模块不应导出数据构造函数。

我们可以制作 Bendo 一个 Category 实例吗?

instance Category Bendo where
  id = Bendo id
  (Bendo f) . (Bendo g) = Bendo (f . g)

的确,这是可能的。我不打算证明这是一个类别,因为它实际上只是专用于 (->) Bool Bool.

-> 类别实例

函子

现在让我们在 UBendo 之间定义一个函子。为此,我们可以使用 Control.Categorical.Functor 中给出的更通用的 Functor 定义。那么,为了使所有这些工作正常,我不得不隐藏 Prelude:

中给出的通常定义
import Control.Category
import Control.Categorical.Functor
import Prelude hiding (id, (.), Functor(..))

我们还需要支持 MultiParamTypeClasses:

{-#LANGUAGE MultiParamTypeClasses #-}

为了实现更通用的 Functor 类型 class,我们需要一个 higher-kinded 类型。同样,让我们​​为此目的生成另一个幻像类型:

data Embed a = Embed deriving (Eq, Show)

这足以定义实例:

instance Functor Embed U Bendo where
  fmap U = Bendo id

这将 U 映射到 Bendo 中的恒等摩尔论。

使用起来有点别扭,但还是可以的:

> (runB $ (fmap U :: Bendo (Embed a) (Embed b))) False
False
> (runB $ (fmap U :: Bendo (Embed a) (Embed b))) True
True

Haskell搞不清楚fmap U的类型是什么,只好说出来。一旦你告诉它结果应该有类型 Bendo (Embed a) (Embed b)fmap 映射 U 到身份态射,然后你可以通过在任一 [=29] 上应用 runB 来验证它=] 或 False.

结论

编程中是否存在函子(不仅仅是内函子)?是的,他们有。

它们有用吗?在我看来,如果你稍微眯起眼睛,那些仿函数只是 'normal' 函数的一个子集。上述仿函数的简化版本就是:

uToBendo :: () -> Bool -> Bool
uToBendo () = id

这只是一个正常的功能。

我要多想想这样看有没有更好用的应用