函子与单子--从范畴论到编程

作为一名计算机工作者,理解范畴论中的函子与单子的目的,还是为了更好的理解函数式编程中的函子与单子,对于我来说,没有比理清两者之间的对应关系,能更好的帮助理解了。

先看一个范畴论中的函子F:

    1. 给定一个范畴C,其元素为{X,Y,Z},态射为{f:X->Y,  g:Y->Z};

    2. 假设F是一个从C到D的函子,记作F:C->D

    3. 那么范畴D的元素为{F(X), F(Y), F(Z)}, 态射为{F(f), F(g)}

    这个函子F的能力概括为两点:

         1. 能把C中的每一个元素,映射到D上,例如Fx:X->F(X)

         2. 能把每一态射从C到映射到D上

简单的替换一下名称:

    1. 给定一个范畴Types,其元素为{String,Enum, Int},态射为{f:String->Enum,  g:Enum->Int};

    2. 假设Fun是一个从Types到FunTypes的函子,记作Fun:Types->FunTypes

    3. 那么范畴FunTypes的元素为{Fun(String), Fun(Enum), Fun(Int}, 态射为{Fun(f), Fun(g)}


对应起来

    以Scala为例,如果把Fun(String)写为Fun[String], 那么这个Fun:Types->FunTypes很接近Scala中的一个Functor了,区别是Scala没有Fun(F)这样的形式,而以Fun.map(f)的形式代替,Fun.map(f: String->Enum): Fun[String] -> Fun[Enum].   

    在Scala中,一个函子F,天然具有把一个类型X映射到另外一个类型F[X]的能力,而map方法提供了映射态射(在编程语言中,就是函数)的能力,满足了函子的的两个能力。

单子呢?

    与函子有一些区别,但根据函子的定义,也不难对应,我总结了一个对应表,总结他们的映射关系,如下表,其中A:C表示A是范畴C的一个元素,F[A]:D表示F[A]是D的一个元素;

小刚的图

推荐阅读更多精彩内容