C 语言里没有现代程序员热衷于讨论的那些东西。程序员
不过,那些东西不是本来就没有么?编程
下面我尝试用 C 语言来写一个单子(Monad)。安全
看下面这段代码:函数
typedef struct { void *thing; } Maybe;
在 C 语言里,这是个结构体,并且是一个彷佛很无聊的结构体。这种结构体能用来作什么呢?指针
能够做为函数的返回值类型。例如:code
Maybe foo(void *thing) { return (Maybe){.thing = thing}; }
假设 foo
像下面这样调用:对象
Maybe a = foo(thing); /* thing 指向前面出现的某个变量 */
那么,当 a.thing
为 NULL
时,表示 foo
函数执行失败,不然表示执行成功。字符串
那么 foo
函数的做用就将一种类型包装成 Maybe
类型。数学
NULL
可能会吓着一些对 C 语言本来就没怎么有好感的人,能够给它换个名字:io
#define Nothing NULL
如今,能够认为当 a.thing
为 Nothing
时,表示 foo
函数执行失败,不然表示执行成功。
接下来,为 Maybe
类型定义这样的函数:
Maybe aha(void * (*f)(void *), Maybe a) { return foo(f(a.thing)); }
这个函数能够将一个函数 f
做用于 a.thing
。假设这个 f
为:
void *textize(void *thing) { int *x = thing; char *text = malloc(32 * sizeof(char)); sprintf(text, "%d", *x); return text; }
有了像 textize
这样的函数,就能够用 aha
函数了,以下:
int a = 2; Maybe x = aha(textize, foo(&a));
结果获得的 x.thing
会指向一个字符串 "2"
。
一个类型再加上一个针对这种类型的 aha
这样的函数,叫函子。
如今,再为 Maybe
类型构造一个函数:
Maybe bar(Maybe a, Maybe (*contuation)(void *thing)) { return a.thing ? contuation(a.thing) : (Maybe){.thing = Nothing}; }
如今,能够宣布有了一个 Maybe
单子。
这个 bar
函数有什么用呢,它可以按照顺序安全地组合一组形状相同的函数,从而起到一个函数的效果。例如,对于下面的三个函数:
Maybe test_a(void *thing) { (*(int *)thing) *= 10; } Maybe test_b(void *thing) { (*(int *)thing) *= 100; } Maybe test_c(void *thing) { (*(int *)thing) += 1000; }
使用 bar
能够把它们按照顺序装配起来:
int a = 2; Maybe x = bar(bar(bar(foo(&a), test_a), test_b), test_c);
结果 x.thing
依然是指向变量 a
的指针,通过一组函数的处理,a
的值变成了 3000。
与上述代码等价的代码,也是 C 程序员惯用的代码以下:
int a = 2; Maybe x = foo(&a); if (x.thing) { x = test_a(x.thing); if (x.thing) { x = test_b(x.thing); if (x.thing) { x = test_c(x.thing); } } }
一个单子,由一种类型以及针对这种类型的相似 foo
、bar
这样的函数构成。
上面我用的 foo
、aha
以及 bar
这些名字,有一些恶意的调侃。值得注意的是,aha
函数实际上能够基于 foo
与 bar
来定义。例如:
Maybe aha(void * (*f)(void *), Maybe a) { return bar(foo(f(a.thing)), foo); }
虽然稍微有点绕圈子,可是 aha
的确是基于 foo
与 bar
定义了出来。这说明了什么呢?
单子的层次高于函子。
假若你能理解上述代码所体现出来的形式,那么上面出现的东西是叫对象、态射、函子、自函子、天然变换、自函子范畴、幺半群、单子,仍是别的什么东西,很重要吗?我觉得这些东西只是对研究数学的人很重要,而对于编程的人来讲……只要你写的代码足够简约,足够具有复用性,那么就一点都不重要。
假若以为很重要,那么好吧,就掰扯一下,以问答的形式。
Q:什么是范畴?
A:范畴由对象和态射构成。例如,C 语言里的数据类型与函数。
Q:什么是函子?
A:函子能够将一种对象变成另外一种对象,将一种态射变成另外一种态射。例如,上面的 Maybe
,将 void *
变成了 Maybe
类型;上面的 aha
将 textize
这种 void * -> void *
的函数变成了 Maybe -> Maybe
的函数。
Q:什么是自函子?
A:将一个范畴里的对象和态射变成这个范畴里的对象和态射的函子就是自函子。做家、画家、歌唱家、演员、舞者、政客、运动员……从事各类职业的人,都是自函子。什么职业都不从事的人也是自函子,他们就是 foo
函数。也有一些不是自函子的人,多数在精神病院里,还有一些在外面跳大神。
Q:什么是自函子范畴?
A:一个自函子构成的范畴。这个范畴里就一个东西,这个自函子。这个范畴里面的态射,叫天然变换。foo
与 bar
都是天然变换。
Q:什么是自函子范畴上的幺半群?
A:将自函子范畴里的全部态射视为集合,那个自函子就消失了。也就是说,自函子转化成了一个态射集合,这个集合叫 Hom-集。想想自个,当你爱上一我的或什么东西的时候,你就消失了,咱们管这个叫忘我。过去的人思慕成仙,要成仙,首先要忘我,而后去搞行为艺术。艺术是永恒的,对吧?这种行为艺术怎么搞呢?须要先搞一个幺元(也有不少人叫单位元)。这个幺元啊,就是眼观鼻、鼻观心、意守丹田。有了幺元,就能够驱动真气,打通任督二脉……作到这两步,就获得了幺半群。
Q:玄学?
A:计算机里运行的玄学。