我所理解的monad(5):自函子(Endofunctor)是什么

经过前面一篇对函子(Functor)的铺垫,我们现在可以看看什么是自函子(Endofunctor)了,从范畴的定义看很简单:

自函子就是一个将范畴映射到自身的函子 (A functor that maps a category to itself)

这句话看起来简单,但有个问题,如何区分自函子与Identity函子?让我们先从简单的“自函数”来看。

自函数(Endofunction)

自函数是把一个类型映射到自身类型,比如Int=>Int, String=>String

注意自函数与Identity函数的差异,Identity函数是什么也不做,传入什么参数返回什么参数,它属于自函数的一种特例;自函数是入参和出参的类型一致,比如 (x:Int) => x * 2(x:Int) => x * 3 都属于自函数:

自函子(Endofunctor)

自函子映射的结果是自身,下图是一个简单的情况:

假设这个自函子为F,则对于 F[Int] 作用的结果仍是Int,对于函数f: Int=>String 映射的结果 F[f] 也仍是函数f,所以这个自函子实际是一个Identity函子(自函子的一种特例),即对范畴中的元素和关系不做任何改变。

那怎么描述出一个非Identity的自函子呢?在介绍范畴在程序上的用法的资料里通常都用haskell来举例,把haskell里的所有类型和函数都放到一个范畴里,取名叫Hask,那么对于这个Hask的范畴,它看上去像是这样的:

先来解释一下(画这个图的时候做了简化),A,B代表普通类型如String,Int,Boolean等,这些(有限的)普通类型是一组类型集合,还有一组类型集合是衍生类型(即由类型构造器与类型参数组成的),这是一个无限集合(可以无限衍生下去)。这样范畴Hask就涵盖了haskell中所有的类型。

对于范畴Hask来说,如果有一个函子F,对里面的元素映射后,其结果仍属于Hask,比如我们用List这个函子:

List[A], List[List[A]], List[List[List[A]]]...

发现这些映射的结果也是属于Hask范畴(子集),所以这是一个自函子,实际上在Hask范畴上的所有函子都是自函子。

我们仔细观察这个Hask范畴的结构,发现它实际是一个fractal结构,所谓fractal(分形),是个很神奇的结构,在自然界也大量存在:

如上面的这片叶子,它的每一簇分支,形状上与整体的形状是完全一样的,即局部与整体是一致的结构(并且局部可以再分解下去)

这种结构在函数式语言里也是很常用的,最典型的如List结构,由headtail 两部分组合而成,而每个tail也是一个List结构,可以递归的分解下去。

我所理解的monad(5):自函子(Endofunctor)是什么》上有7个想法

  1. 讨论范畴的时候最重要的就是明确范畴里的对象是什么、对像间的关系(态射、箭头)又是什么。

    我有一个疑问:真的可以根据对象的构造方式而不是对象本身来区分范畴吗?或者说对象的构造方式也是对象的属性?

    例如这里讨论的对象都是proper type,没有讨论态射,那么从范畴C3和C4里的对象和态射来看,他们其实都是对象为proper type的范畴。

    • 对于自函子,我是对比着自函数来理解的,今天在微博上跟九爪交流了一下,发觉我理解的有问题。不应该按照范畴的kind来给范畴分类,这篇blog稍后我会修改一下。

  2. “假设这个自函子为F,则对于 F[Int] 作用的结果仍是Int,对于函数f: Int=>String 映射的结果 F[f] 也仍是函数f,所以这个自函子实际是一个Identity函子(自函子的一种特例),即对范畴中的元素和关系不做任何改变。”

    这一段写得很难以理解

    • 理解自函子的话,可以先降一个维度,对比一个 返回自身结果的函数,传给它什么,返回的也是什么。

      只不过 函子(Functor) 是面向类型编程的,F[_] 是一个函子的话,它可以把 String 映射为 F[String],
      把 Int=>String 映射为 F[Int=>String] ,如果一个函子 F[_] 对 Int 映射的结果 F[Int] 也等价于 Int 那么它就是个自函子。

    • 其实理解Hask那张图,比单纯理解那句话更简单。那张图的描述更接近我们实际使用的场景,比如Some(1).flatMap(i => Some(i + 1))。而那张图描述的是一种特殊的自函子。

    • 恒等函子Identity functor:把范畴中的对象和态射都对应到其自身。
      这里的例子里面就是
      ‘类型int’(对象)映射到自己,
      ‘函数f:int=>string’(态射)映射到自己,
      这2个过程的形式表达就是F[Int]和F[f]。
      这个映射过程中函子做了件看起来很无聊的事情,实现了一个自身到自身的映射,也就是恒等。

发表评论

电子邮件地址不会被公开。 必填项已用*标注