身為一名開發者,如果被問到「程式是什麼?」該如何回答呢?若進一步地被問到「程式語言又是什麼?」答案又是什麼呢?什麼又是程式設計呢?

如果使用具備一級函式特性的語言,我們可以從中構造微型語言,而探討這微型語言是什麼,或許可以得到答案!

一級函式的規則

在之前〈ES6與lambda演算〉中,談到了如何使用ES6箭號函式,為自然數、後續操作、加法等編碼,若持續這樣的編碼過程,開發者漸漸地會感覺到一個微型語言正在成形。

因為此時使用的是JavaScript箭號函式,被構造出來的微型語言,可以在JavaScript環境中直接執行,也就是說,在JavaScript語言中創造了另一門語言?

從語言中提供一級函式特性的角度來看,確實是有這樣的可能性。

現今不少具有一級函式特性的語言,也會以此為亮點來吸引開發者,像是可以基於這些語言,來構造專屬的領域特定語言(Domain Specific Language,DSL)這樣的口號,例如Scala、Ruby等語言就是如此。

從廣義角度來看,開發程式庫,甚至是開發一個具有流程約束的框架,都算是在構造一門語言,只是成品看來不像原生語言的語法,於是給了一些籠統的名詞,像是程式庫、框架、API之類的稱呼。

由於箭號函式構造的各種資料編碼,像是yes、no、when等,在JavaScript中終究是函式,因而呼叫時必須使用大括號,與原生的if、true、false等關鍵字相比,風格上還是不盡相同,充其量就是JavaScript建立的程式庫、框架、API(要怎麼稱呼都可以)。

這麼看來,有些語言中函式的呼叫上,可以不使用大括號,使得呼叫函式呼叫時,像是原生語言語法的DSL。說穿了也一樣,就是程式庫之類的東西。只不過用這些名詞看來有點Low,就取了較有亮點的DSL名稱罷了……

然而,若能完全使用箭號函式來寫程式,就不用與原生的if、true、false等相比,大括號就不是問題,就像個DSL了。

等一下!能完全用箭號函式來寫程式?這不就代表著,箭號函式的規則就是一門程式語言?內建在JavaScript之中?是的!

JavaScript早就內建了一門微型語言,對此也不用感到驚訝,語言中內建微型語言不是新鮮事。

就算沒有一級函式特性,多數語言都會支援一種微型語言,那就是規則表示式(Regular expression),儘管能力有限,規則表示式確實是一種語言,主要用來進行字串匹配的語言。

形式化

語言會有語法規則,一級函式其實代表著某種形式的lambda表示式,最簡單的lambda表示式,只有三條語法規則。

規則一是x變數,名稱可以是單一字元或字串(像是map),用來代表參數或數學/邏輯值(值也是lambda表示式)。規則二是(λx.M)抽象,也就是函式定義,x被綁定在表示式中,M會是個lambda表示式。規則三是(M N)套用,M、N是lambda表示式,N被當成是M的引數。

顯然地,lambda表示式就是運算表示式,對照至JavaScript箭號函式,規則一會是x,規則二為(x => M),規則三就是M(N)。既然箭號函式可以完全對應lambda表示式,將箭號函式寫出來的程式,完全改用x、(λx.M)、(M N)來表示,自然也是程式,而且與JavaScript執行環境完全無關。

也就是說,基本上,任何語言撰寫的程式,都可以使用lambda表示式來表達,lambda表示式就是運算式,這就直接說明了兩件事:「語言定義形式化的規則,程式就是運算的形式化。」

什麼叫「形式化」?「加1的函式」是人類對此運算的直覺描述,(λx.x + 1)的形式,清晰地表示了此運算是對x加1;而「兩數相減的函式」是人類對此運算的直覺描述,(λx.(λy.x - y))的形式,清楚地表示了此運算是以x減去y,

試著用更精確的人類描述,來表示(λx.(λy.x - y))也不是不行,只不過得到冗長(且可能包含岐義)的語句罷了。

可運算的程式

任何語言撰寫的程式,都可以使用lambda表示式來表達,定義lambda表示式規則的lamdbda演算就是語言,而從lamdbda演算的角度來看,程式就是個規模或大或小的運算式罷了。

實際上,使用任何程式語言,針對需求來撰寫程式的過程,就是在使用該語言的規則,將直覺描述的需求進行形式化的過程,只不過,現代多半使用高抽象的高階語言,運用更接近人類描述的語法,因而語言被單純看成是人類與電腦溝通的工具,忽略了形式化的過程,掩蓋了運算的本質,甚至有不少人將運算與程式分開看待。

直接使用lambda演算的概念來編寫程式,只是將形式化、運算的本質拉出來,直接將程式看成規模或大或小的運算式,如此就能直接清晰地界定什麼是可運算的函式,以及什麼是可運算的問題。

若使用高階程式語言,就是將使用直覺描述的問題,運用該語言的規則將每個細節形式化,例如,「兩數餘除的函式」的直覺描述,以JavaScript,來形式化為let mod = (m, n) => n <= m ? mod(m - n, n) : m,清晰地表示出餘除運算該是什麼形式。

若最後能得到一個無岐義、無矛盾的形式化定義,那就是程式了,一個可運算的程式!也就是說,並非任何直覺描述的問題,都能化為可運算的程式,例如絕對不說真話的騙子告訴你:「我現在正在說謊!」,想要將判斷他說的是真話或假話這件事形式化,有可能做到嗎?

人們多半直覺的反應是,既然騙子絕對不說真話,他說的必然是假話,事實並非如此單純,『絕對不說真話的騙子告訴你:「我現在正在說謊!」』這描述自相矛盾,是邏輯上無從判斷騙子該話是真或假的悖論!

這並不是人類話語才會有的矛盾問題。直覺往往不準確,想要開發出一個通用程式,可以接受任意程式與某一輸入,判斷它能否顯示HelloWorld,這個需求也是不可能的!

形式化的能力

維基百科〈程式語言〉條目一開始,就寫到:「程式語言是用來定義電腦程式的形式語言」。運用程式語言撰寫程式,就是將直覺描述的需求形式化,邏輯是形式化過程必需的能力之一,寫出a > 10 and a < 5的開發者(這並不罕見),就是邏輯能力不足的表現。

能將問題以明確、有限、有效方式形式化之能力,就是構造演算法之能力;能將資料如何儲存、組織進行形式化之能力,就是建立資料結構之能力;可基於物件導向、函數式等典範,可使用這類高階語言進行設計,都是某種形式化能力之展現。

程式就是某種形式化後的產物,這產物的形式化是否正確、可否計算,就代表著開發者從事程式設計之能力,形式化能力可以透過訓練來增強,像是持續地從各式語言、演算法、資料結構、物件導向、函數式甚至於lambda演算中學習與思考,這就是程式設計了!

作者簡介


Advertisement

更多 iThome相關內容