上週末讀了 John Backus 的 1977 年圖靈獎獲獎演講論文,很受震撼。CS 學科依然很年輕,有些基礎性問題探索了四十多年了還沒結果。John Backus 在四十多年前提出的思想,放到如今都依然超前。我之前發表過《如何在 JS 代碼中消滅 for 循環》,看起來比較離經叛道故意搞爭議。沒想到消滅 for 循環這件小事,早在四十多年前就有現代編程語言的祖師爺之一明確支持過了……javascript
這篇文章只是對 John Backus 論文的淺顯介紹。我也想深度介紹一下,但功底有限。論文中的 formal proof 部分我看不懂,就跳過了;剩下部分比較好懂,可是有些上下文知識我不知道,John 的各類論斷無從擴展思考,我就只轉述。若是你英文過關,建議直接讀原論文。另外,筆者還只是個 CS 小學生,若是有理解誤差,還望各路大神輕噴。前端
在得到圖靈獎以前,John Backus 主要的貢獻有這些:java
他在程序語言設計上,以及在天然語言和計算機語言的交互上都有開創性貢獻,把他列爲現代主流(指令式和過程式)編程語言的祖師爺之一併不爲過。express
他由於上述貢獻而被授予 1977 年圖靈獎。然而,在他的獲獎演講裏,他否認了本身的成就,併爲發明了 Fortran 而感到遺憾……編程
那篇演講論文題爲 Can Programming Be Liberated from the von Neumann Style? A Functional Style and Its Algebra of Programs。這種標題放到四十年後的今天估計也會引戰的,下面我介紹下這篇論文主要講了什麼。bash
論文標題裏面的 von Neumann Style 指的是受馮諾依曼計算機架構影響的編程風格。馮諾依曼架構是上世紀四十年代,馮諾依曼爲了解決當時 ENIAC 計算機的低效而提出的一種架構設計。在馮諾依曼架構下,一臺計算機包括:架構
能夠看出馮諾依曼架構是現代計算機的主流架構,目前的計算機也是這種架構。app
John Backus 認爲,馮諾依曼架構是爲了解決四十年代的問題而提出的,這種架構在當時是簡潔優雅而高效的。可是到了七十年代,馮諾依曼架構要解決的問題不存在了,繼續沿用這種架構會帶來一些問題。(筆者注:既然馮諾依曼架構四十年前就過期了,爲何到如今依然是主流?是 John Backus 錯了,仍是由於 John Backus 後來解釋的那樣,馮諾依曼架構催生了馮諾依曼編程語言,然後者又綁架了計算機設計者,讓非馮諾依曼架構難以誕生?)框架
John Backus 認爲馮諾依曼架構的問題有這些:編程語言
一,馮諾依曼瓶頸 (von Neumann Bottleneck)
在馮諾依曼架構下,程序和數據儲存在內存裏,處理器要經過一個管道來從內存裏讀取數據,計算完成後再經過管道把結果送回內存。這個管道就是個瓶頸。無論處理器效率多高,計算的速度取決於管道傳輸數據的速度。
二,馮諾依曼架構桎梏了編程語言
爲了適應馮諾依曼架構,編程語言變得臃腫而低效。具體表現有這些:
下面繼續深刻前面提到的馮諾依曼風格語言的問題。
John Backus 認爲,賦值語句就是語言上的馮諾依曼瓶頸。在馮諾依曼瓶頸裏傳輸的數據,很大一部分根本不是有效數據,而是這些數據的名字,以及用來計算這些名字的操做指令和數據。這無疑是低效的。更重要的是,這種編程風格帶來的思惟的瓶頸,讓咱們只能在逐字解釋的框架下去思考,不鼓勵咱們用更大的概念單元 (conceptual unit) 去思考編程問題。(這個論斷當下依然有效)
進一步,John Backus 指出
Our fixation on von Neumann languages has continued the primacy of the von Neumann computer, and our dependency on it has made non-von Neumann languages uneconomical and has limited their development. The absence of full scale, effective programming styles founded on non-von Neumann principles has deprived designers of an intellectual foundation for new computer architectures.
若是這個論斷是正確的,John Backus 解釋了在他發表這篇論文四十多年後,咱們尚未突破馮諾依曼瓶頸的緣由。
當時 LISP 已經誕生了,可是 John Backus 認爲 pure LISP 仍是帶着一堆傳統編程特徵的擴展。
在這篇論文裏面,John Backus 專門挑了 for 循環來講明馮諾依曼語言的問題。(我要是早點知道就行了 ^_^) 他給的例子以下:
c := 0
for i := 1 step 1 until n do
c := c + a[i]xb[i]
複製代碼
我不知道這是什麼語言,不過很明顯這是個 for 循環。John Backus 以下分析上面這段 for 循環的問題:
a) Its statements operate on an invisible "state" according to complex rules. 根據複雜的規則去操做看不到的狀態。
b) It is not hierarchical. Except for the right side of the assignment statement, it does not construct complex entities from simpler ones. (Larger programs, however, often do.) 程序沒有層級,沒有組合。
c) It is dynamic and repetitive. One must mentally execute it to understand it. 指令動態而重複,讀代碼的人要在腦子裏執行一遍才懂。
d) It computes word-at-a-time by repetition (of the assignment) and by modification (of variable i). 經過重複和改變值來逐字解釋。
e) Part of the data, n, is in the program; thus it lacks generality and works only for vectors of length n. 數據和程序強耦合,難以複用。
f) It names its arguments; it can only be used for vectors a and b. To become general, it requires a procedure declaration. These involve complex issues (e.g., call-by-name versus call-by-value). 給變量命名,使程序不夠通用。John Backus 的這個主張後來即便在函數式語言的探索裏也沒被採納。這裏我不太懂。
g) Its " housekeeping" operations are represented by symbols in scattered places (in the for statement and the subscripts in the assignment). housekeeping 能夠理解爲具體的程序執行。for 循環把這些執行指令散佈在各處,難以鞏固成可通用的,單一的操做符。
針對上面提到的問題,John Backus 提議的語法以下:
Def Innerproduct
= (Insert +) . (ApplyToAll x) . Transpose
複製代碼
上面的等號是三個橫線的,並非賦值語句。
用 JS 能夠藉助 Ramda 翻譯以下:
const Innerproduct = R.compose(
R.sum,
R.map(R.apply(R.multiply)),
R.transpose,
)
複製代碼
Ramda 函數的具體實現也是馮諾依曼風格的,另外賦值操做在 John Backus 的提議裏也是禁止的,這裏就忽略了,只看高階抽象部分。
John Backus 認爲第二種風格的優點以下:
a) It operates only on its arguments. There are no hidden states or complex transition rules. 引用透明,只操做參數。沒有隱藏狀態,沒有複雜的狀態轉移規則。
b) It is hierarchical, being built from three simpler functions and three functional forms. 有層級,可組合。
c) It is static and nonrepetitive, in the sense that its structure is helpful in understanding it without mentally executing it. 靜態,非重複。不用在腦子裏執行一遍程序,僅看程序結構也能理解程序意圖。
d) It operates on whole conceptual units, not words; it has three steps; no step is repeated. 以完整的概念單元爲操做對象,而不是逐詞解釋。三個步驟各自獨立而不重複。
e) It incorporates no data; it is completely general; it works for any pair of conformable vectors. 程序不包含數據,徹底通用。我理解的函數式編程的 point free 應該符合這裏的描述。
f) It does not name its arguments; it can be applied to any pair of vectors without any procedure declaration or complex substitution rules. 不給參數命名。如上所述,目前多數語言沒有作到,這裏不作探討。
g) It employs housekeeping forms and functions that are generally useful in many other programs; in fact, only + and × are not concerned with housekeeping. These forms and functions can combine with others to create higher level housekeeping operators. 程序指令通用而可組合,能夠組合成更高階的操做符。
John Backus 最後提到了他那個時代一些替代馮諾依曼架構的嘗試,如 applicative machine,這種架構模型沒有儲存器和地址寄存器,也就沒有馮諾依曼瓶頸。John Backus 認爲 applicative 風格程序比馮諾依曼程序更強大。這部分我不懂,就很少展開了。
John Backus 的這篇論文發表以後,學術界開始活躍探索函數式編程,Haskell 的誕生就受此影響。不過,替代馮諾依曼架構的方案目前好像還遠不夠成熟。語言層面上,四十多年來有了更多的突破馮諾依曼風格桎梏的嘗試,但距離催生新的計算機設計架構還很遙遠。
插個廣告
螞蟻保險體驗與社區技術組招高級前端開發工程師。有興趣的同窗聯繫 ray.hl@alipay.com