文章目錄
- 1.你知道些什麼
- 2. 反向運行
- 3. miniCoq
- 5.獨立思維
1.你知道些什麼
Dan Friedman 是 IU 的教授,程式語言界的元老之一,Lisp (Scheme) 語言的主要研究者之一,《The
Little Schemer》(前身叫《The Little Lisper》)的作者。他對程式語言有非常深刻的理解。Haskell
所用的 lazy evaluation 模型,最早就是他在 1976 年在與 David Wise 合寫的論文“CONS should
not Evaluate its
Arguments”中提出來的。他並不是我正式的導師,但他是這一生中教會我最多東西的人,所以我想寫一些關於他的小故事。也許你能從中看出,一個真正的教育者是什麼樣子的。我來
IU 之前,一位師兄告訴我,Dan Friedman 就像指環王裡的甘道夫
(Gandalf),來了之後發現確實很像。
第一次在辦公室見到 Friedman
的時候,他對我說:“來,給我講講你知道些什嗎?”我自豪地說:“我在 Cornell 上過研究生的程式語言課,會用 ML 和
Haskell,看過 Paul Graham 的 On Lisp,Peter Norvig 的 Paradigms of
Artificial Intelligence Programming, Richard Gabriel
的一些文章...”他看著我微笑:“不錯,你已經有一定基礎……”
這麼幾年以後,我才發現他善良的微笑裡面,隱藏著難以啟齒的秘密:當時的我是多麼的幼稚!在他的這種循循善誘之下,我才逐漸的明白了,知識的深度是無止境的。
2. 反向運行
Dan Friedman 已經遠遠超過了退休年齡,仍然堅持教學。他的本科生程式語言課程 C311 是 IU
的“星級課程”。我最敬佩的,是他那孩子般的好奇心和探索精神。幾乎每一年的
C311,他都會發明不同的東西來充實課程內容。有時候是一種新的邏輯程式設計語言 (類似 Prolog),有時候是些小技巧 (比如把
Scheme 編譯成 C 卻不會堆疊溢位),等等……
Friedman研究一個東西的時候總是全身心的投入,執著的熱愛。自從開始搞這個叫 miniKanren 的邏輯程式設計語言,Friedman
多了一句口頭禪:“Does it run
backwards?”(能反向運行嗎?)因為邏輯式的語言(像Prolog)的程式,都是能“反向運行”的。普通程式語言寫的程式,如果你給它一個輸入,它會給你一個輸出。但是邏輯式語言很特別,如果你給它一個輸出,它可以反過來運行,給你所有可能的輸入。但是 Friedman
真的走火入魔了。不管別人在講什麼,經常最後都會被他問一句:“Does it run
backwards?”讓你哭笑不得。
Friedman
有一個本領域的人都知道的“弱點”——他不喜歡靜態類型系統 (static type system)。其實 Scheme
專家們大部分都不喜歡靜態類型系統。為此,他深受“類型專家”們的誤解甚至鄙視,可是他都從容對待之。
有一次在他的進階課程 B621 上,他給我們出了一道題:用
Scheme 實現 ML 和 Haskell
所用的 Hindley-Milner
類型系統。這種類型系統的工作原理一般是,輸入一個程式,它經過對程式進行類型推導(type
inference),輸出一個類型。如果程式裡有類型錯誤,它就會報錯。由於之前在 Cornell 用 ML
實現過這東西,再加上來到 IU 之後對抽象解釋 (abstract interpretation)
的進一步理解,我很快做出了這個東西,而且比在 Cornell 的時候做的還要優雅。
他知道我做出來了,很高興的樣子,讓我給全班同學(也就8,9個人)講我的做法。當我自豪的講完,他問:“Does
it run backwards?
如果我給它一個類型,它能自動產生出符合這個類型的程式來嗎?”我愣了,欲哭無淚啊,“不能……”他往沙發靠背上一躺,得意的笑了:“我的系統可以!哈哈!我當年寫的那個類型系統比你這個還要短呢。我早就知道這些類型系統怎麼做,可我就是不喜歡。哈哈哈哈……”
前幾天看了 Stephen Kleene
的一篇論文,才發現原來他說的這種由類型反向算出程式的做法叫做
“realizability”,是一個很深刻的理論,可以用來協助自動證明數學定理。而我後來對類型系統的進一步研究顯示,Hindley-Milner
類型系統確實有很多不必要的問題,才導致了他不喜歡。
他就是這樣一個老頑童。他喜歡先把你捧上天,再把你打下來,讓你知道天外有天
3. miniCoq
你永遠想象不到 Dan Friedman
的思想的極限在哪裡。當你認為他是一個函數式語言專家的時候,他設計了 miniKanren,一種邏輯式程式設計語言
(logic programming language),並且寫出 《The Reasoned
Schemer》,用於教授邏輯編程。當你認為他不懂類型系統的時候,他開始搗鼓最尖端的 Martin-Löf類型理論,並且開始設計機器證明系統。而他做這些,完全是出於自己的興趣。他從來不在乎別人在這個方向已經做到了什麼程度,卻經常能出乎意料的簡化別人的設計。
有一次系裡舉辦教授們的“閃電式演講”(lightening
talk),每位教授只有5分鐘時間上去介紹自己的研究。輪到 Friedman
的時候,他慢條斯理的走上去,說:“我不著急。我只有幾句話要說。我不知道我能不能拖夠5分鐘……”大家都笑了。他接著說:“我現在最喜歡的東西是 Curry-Howard
correspondence和定理證明。我覺得現在的機器證明系統太複雜了,比如 Coq 有 nnnnn 行代碼。我想在 x
年之內,簡化 Coq,得到一個 miniCoq……”
miniCoq...
聽到這個詞全場都笑翻了。為什麼呢?自己去聯想Friedman 的 miniCoq” 成為了 IU 的程式語言學生茶餘飯後的笑話。</p>
<p>但是他沒有吹牛,他總是說到做到。他已經寫出一個簡單的定理證明工具叫
JBob(迫於社會輿論壓力,不能叫 miniCoq),而且正在寫一本書叫 《The Little
Prover》,用來教授最重要的定理證明思想。他開始在 C311 上給本科生教授這些內容。我看了那本書的初稿,獲益至深,那是很多
Coq
的教材都不涉及的最精華的道理。它不僅教會我如何<b>使用</B>定理證明系統,而且教會了我如何<b>設計</B>一個定理證明系統。我對他說:“你總是有新的東西教給我們。每隔兩年,我們就得重新上一次你的課!”</p>
<h3>4. C311</h3>
<p>當我剛從 Cornell 轉學到 IU 的時候,Dan
Friedman 叫我去上他的研究生程式語言課 B521。我當時以自己在 Cornell
上過程式語言課程為由,想不去上他的課。Friedman 把我叫到他的辦公室,讓我在他旁邊坐下來,和藹的對我說:“王垠,我知道你在
Cornell 上過這種課。我也知道 Cornell 是比 IU
好很多的學校。可是每個老師的教學方法都是不一樣的,你應該來上我的課。我和我的朋友們在這裡做教授,不是因為喜歡這個學校,而是因為我們的家人和朋友都在這裡。”後來由於跟
Amr Sabry(我現在的導師)的課程 B522 時間重合,他特別安排我坐在本科生的 C311
的課堂上,卻拿研究生課程的學分。後來發現,這兩門課的內容基本沒有區別,只不過研究生的作業要多一些。</p>
<p>在第一堂課上,他說了一句讓我記憶至今的話:“《The Little Schemer》和《Essentials of Programming Languages》是這門課的參考教材,但是我上課從來不講我的書裡的內容。”剛一開始,我就發現這門課跟我在 Cornell學到的東西很不一樣。雖然有些概念,比如 closure,CPS,我在 Cornell
都學過,在他的課堂上,我卻看到這些概念完全不同的一面,以至於我覺得其實我之前完全不懂這些概念!這是因為在 Cornell
學到這些東西的時候只是用來應付作業,而在 Friedman
的課上,我利用它們來完成有實際意義的目標,所以才真正的體會到這些概念的內涵和價值。</p>
<p>一個例子就是課程進入到沒幾個星期的時候,我們開始寫解譯器來執行簡單的 Scheme
程式。然後我們把這個解譯器進行 CPS 變換,引入全域變數作為" cps continuation machine jvm cpu olivier danvy>機器並不是計算的本質。機器可以用任何可行的技術實現,比如整合電路,雷射,分子,DNA……
但是無論用什麼作為機器的材料,我們所要表達的語義,也就是計算的本質,卻是不變的。
而這些還不是我那屆 C311 全部的內容。後半學期,我們開始學習 miniKanren,一種他自己設計的用於教學的邏輯式語言 (logic programming
language)。這個語言類似 Prolog,但是它把 Prolog
的很多缺點給去掉了,而且變得更加容易理解。教材是免費送給我們的《The Reasoned
Schemer》。在書的最後,兩頁紙的篇幅,就是整個 miniKanren
語言的實現!我學得比較快,後來就開始搗鼓這個實現,把有些部分重新設計了一下,然後加入了一些我想要的功能。這樣的教學,給了我設計邏輯式語言的能力,而不只是停留於一個使用者。這是學習Prolog 不可能做到的事情,因為 Prolog實現的複雜性,會讓初學者無從下手,只能停留在使用者的階段。
我很幸運當初聽了他的話,去上了這門課,否則我就不會是今天的我。
5.獨立思維
Dan Friedman
是一個不隨波逐流,有獨立思想的人。他的眼裡容不下過於複雜的東西,他喜歡把一個東西簡化到容得進幾行程式,把相關的問
題理解得非常清楚。他的書是一種獨特的“問答式”的結構,很像孔夫子或者蘇格拉底的講學方式。他的教學方式也非常獨特。這在本科生課程
C311 裡已經有一些表現,但是在研究生的課程 B621 裡,才全部的顯示出來。
我寫過的最滿意的一個程式,自動CPS 變換,就是在 C311 產生的。在 C311 的作業裡,Friedman
經常加入一些“智力題”(brain
teaser),做出來了可以加分。因為我已經有一定基礎,所以我有精力來做那些智力題。開頭那些題還不是很難,直到開始學
CPS 的時候,出現了這麼一道:“請寫出一個叫 CPSer 的程式,它的作用是自動的把 Scheme
程式轉換成 CPS 形式。”那次作業的其它題目都是要求手動把程式變成 CPS
形式,這道智力題卻要求一個自動的——用一個程式來轉換另一個程式。
我覺得很有意思。如果能寫出一個自動的 CPS
轉換程式,那我豈不是可以用它完成所有其它的題目了!所以我就開始搗鼓這個東西,最初的想法其實就是“類比”一個手動轉換的過程。然後我發現這真是個怪物,就那麼幾十行程式,不是這裡不對勁,就是那裡不對勁。這裡按下去一個
bug,那裡又冒出來一個,從來沒見過這麼麻煩的東西。我就跟它死磕了,廢寢忘食幾乎一星期。經常走進死胡同,就只有重新開始,不知道推翻重來了多少次。到快要交作業的時候,我把它給弄出來了。最後我用它產生了所有其它的答案,產生的
CPS
代碼跟手工轉換出來的看不出任何區別。當然我這次我又得了滿分(因為每次都做智力題,我的分數總是在100以上)。
作業發下來那天下課後,我跟 Friedman 一起走回
Lindley Hall(IU 電腦系的樓)。半路上他問我:“這次的 brain teaser
做了沒有。”我說:“做了。這是個好東西啊,幫我把其它作業都做出來了。”他有點吃驚,又有點將信將疑的樣子:“你確信你做對了?”我說:“不確信它是完全正確,但是轉換後的作業程式全都跟手工做的一樣。”走回辦公室之後,他給了我一篇30多頁的論文
“Representing control: a study of the CPS
transformation”,作者是 Olivier Danvy 和 Andrzej
Filinski。然後我才瞭解到,這是這個方向最厲害的兩個人。正是這篇論文,解決了這個懸而不決十多年的難題。而自動的 CPS
轉換,可以被用於實現高效的函數式語言編譯器。Princeton 的 Andrew Appel 教授寫了一本書叫《Compiling
with Continuations》,就是專門講這個問題的。而 Amr Sabry(我現在的導師)當年的博士論文就是一個比 CPS
還要簡單的變換(叫做 ANF)。憑這個東西,他幾乎滅掉了這整個 CPS 領域,並且拿到了終身教授職位。Friedman
啊,把這樣一個問題作為“智力題”,真有你的!我開玩笑地對他說:“我保證,我不會把這個程式開源,不然你以後的 C311
學生們就可以拿來作弊了 :-)”回到家,我開始看那篇 Danvy 和 Filinski 的論文。這篇 1991 年的論文的想法,是從
1975 年一篇 Gordon Plotkin的論文的基礎上,經過一系列繁瑣的推導得出來的。而它最後的結果,幾乎跟我的程式一模一樣,只不過我的程式可以處理更加複雜的
Scheme,而不只是 lambda calculus。我之前完全不知道 Plotkin
的做法,就直接得到了最新的結果。這是我第一次認識到自己頭腦的威力。
第二個學期,當我去上 Friedman 的進階課程 B621
的時候,他給我們出了同樣的題目。兩個星期下來,沒有其它人真正的做對了。最後他對全班同學說:“現在請王垠給大家講一下他的做法。你們要聽仔細了哦。這個程式價值100美元!”
而這還不是 B621 的全部,每一個星期,Friedman
會在黑板上寫下一道很難的題目。他不讓你看書或者看論文。他有時甚至不告訴你題目裡相關概念的名字,或者給它們起個新名字,讓你想查都查不到。他要求你完
全靠自己把這些難題解出來,下一個星期的時候在黑板上給其它同學講解。他沒有明確的評分標準,讓你感覺完全沒有成績的壓力。這些題目包括很難的一些問題,
比如 church
numeral 的前驅 (predecessor)。這個問題,當年是 Stephen Kleene
花了三個月冥思苦想才做出來的。不幸的是,我早就學到了 Kleene
的做法,造成了思維的定勢,所以這個訓練當時對我來說失去了意義。而我們班上卻有一個數學系的同學,出人意料的在一個星期之內做出了一個比
Kleene 還要簡單的方法。其它的問題包括從 lambda calculus
到 SKI combinator 的編譯器,邏輯式(可逆)CPS
變換,實現 Hindley-Milner 類型系統,等等。我發現,就算自認為明白了的東西,經過一番思索,認識居然還可以更進一步。
當然,重新發明東西並不會給我帶來論文發表,但是它卻給我帶來了更重要的東西,這就是獨立的思考能力。一旦一個東西被你“想”出來,而不是從別人那裡
“學”過來,那麼你就知道這個想法是如何產生的。這比起直接學會這個想法要有用很多,因為你知道這裡面所有的細節和犯過的錯誤。而最重要的,其實是由此得
到的直覺。如果直接去看別人的書或者論文,你就很難得到這種直覺,因為一般人寫論文都會把直覺埋藏在一堆符號公式之下,讓你看不到背後的真實想法。如果得到了直覺,下一次遇到類似的問題,你就有可能很快的利用已有的直覺來解決新的問題。
而這一切都已經發生在我身上。比如,在聽說 ANF
之後,我沒有看 Amr Sabry 的論文,只把原來的 CPSer 程式改了一點點,就得到了 ANF
變換,整個過程只花了十幾分鐘。而在 R. Kent Dybvig 的編譯器課程上,我利用 CPS 變換裡面的直覺,改造和合并了
Dybvig 提供的編譯器架構的好幾個 pass,使得它們變得比原來短小好幾倍,而且產生很不錯的代碼。
現在我仍然是這樣,喜歡故意重新發明一些東西,探索不止一個領域。這讓我獲得了直覺,不再受別人思想的限制,節省了看論文的時間,而且多了一些樂趣。一個問題,當我相信自己能想得出來,一般都能解決。雖然我經常不把我埋頭做出來的東西放在心上,把它們叫做“重新發明”(reinvention),但是出乎意料的是,最近我發現這裡面其實很是隱藏了一些真正的發明。我準備慢慢把其中一些想法發掘整理出來,發表成論文,或者做成產品。
俗話說,“授人以魚,不如授人以漁。”就是這個道理吧。Dan Friedman,謝謝你教會我釣魚。