單子,想弄不懂都很難

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.thingNULL 時,表示 foo 函數執行失敗,不然表示執行成功。字符串

那麼 foo 函數的做用就將一種類型包裝成 Maybe 類型。數學

NULL 可能會嚇着一些對 C 語言本來就沒怎麼有好感的人,能夠給它換個名字:io

#define Nothing NULL

如今,能夠認爲當 a.thingNothing 時,表示 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);
                }
        }
}

一個單子,由一種類型以及針對這種類型的相似 foobar 這樣的函數構成。

上面我用的 fooaha 以及 bar 這些名字,有一些惡意的調侃。值得注意的是,aha 函數實際上能夠基於 foobar 來定義。例如:

Maybe aha(void * (*f)(void *), Maybe a)
{
        return bar(foo(f(a.thing)), foo);
}

雖然稍微有點繞圈子,可是 aha 的確是基於 foobar 定義了出來。這說明了什麼呢?

單子的層次高於函子。

假若你能理解上述代碼所體現出來的形式,那麼上面出現的東西是叫對象、態射、函子、自函子、天然變換、自函子範疇、幺半羣、單子,仍是別的什麼東西,很重要嗎?我覺得這些東西只是對研究數學的人很重要,而對於編程的人來講……只要你寫的代碼足夠簡約,足夠具有複用性,那麼就一點都不重要。

假若以爲很重要,那麼好吧,就掰扯一下,以問答的形式。

Q:什麼是範疇?

A:範疇由對象和態射構成。例如,C 語言裏的數據類型與函數。

Q:什麼是函子?

A:函子能夠將一種對象變成另外一種對象,將一種態射變成另外一種態射。例如,上面的 Maybe,將 void * 變成了 Maybe 類型;上面的 ahatextize 這種 void * -> void * 的函數變成了 Maybe -> Maybe 的函數。

Q:什麼是自函子?

A:將一個範疇裏的對象和態射變成這個範疇裏的對象和態射的函子就是自函子。做家、畫家、歌唱家、演員、舞者、政客、運動員……從事各類職業的人,都是自函子。什麼職業都不從事的人也是自函子,他們就是 foo 函數。也有一些不是自函子的人,多數在精神病院裏,還有一些在外面跳大神。

Q:什麼是自函子範疇?

A:一個自函子構成的範疇。這個範疇裏就一個東西,這個自函子。這個範疇裏面的態射,叫天然變換。foobar 都是天然變換。

Q:什麼是自函子範疇上的幺半羣?

A:將自函子範疇裏的全部態射視爲集合,那個自函子就消失了。也就是說,自函子轉化成了一個態射集合,這個集合叫 Hom-集。想想自個,當你愛上一我的或什麼東西的時候,你就消失了,咱們管這個叫忘我。過去的人思慕成仙,要成仙,首先要忘我,而後去搞行爲藝術。藝術是永恆的,對吧?這種行爲藝術怎麼搞呢?須要先搞一個幺元(也有不少人叫單位元)。這個幺元啊,就是眼觀鼻、鼻觀心、意守丹田。有了幺元,就能夠驅動真氣,打通任督二脈……作到這兩步,就獲得了幺半羣。

Q:玄學?

A:計算機裏運行的玄學。

相關文章
相關標籤/搜索