在為各種資料編碼的過程中,漸漸感覺到一個微型語言正在成形,因為 JavaScript 提供箭號函式,令這微語言可以在 JavaScript 的環境中執行,也就是說,在 JavaScript 語言中創造了另一門語言!?
從語言中提供有一級函式特性的角度來看,確實是有這樣的可能性存在,現今不少具有一級函式特性的語言,也會以此為亮點,像是可以基於這些語言,來構造專屬的領域特定語言(Domain Specific Language, DSL),像是 Scala、Ruby 等就是如此。
從廣義角度來看,開發程式庫,甚至是開發一個具有流程約束的框架,都算是在構造一門語言,雖然成品看來不像是原生語言的語法,於是給了這些成員一些籠統的名詞,像是程式庫、框架、API 之類的。
若是如此,先前文件中建立的各種資料編碼、yes
、no
、when
等,就像是 JavaScript 建立起來的…程式庫、框架、API…你要怎麼稱呼都行…這麼看來,那些像是原生語言語法的 DSL,說穿了也一樣,就是程式庫、框架、API…只不過用這些名詞看來有點 Low,就取了較有亮點 DSL 罷了…
真正的微語言,其實是「一級函式的規則」!
JavaScript 一開始就有一級函式,也就是說,JavaScript 早就內建了一個微語言在自身之中,只不過,在 ES6 提供箭號函式,function
這關鍵字太長了,用它來談之前的文件,眼睛會花的更嚴重!不過,真要用還是可以的!
語言中內建微語言早就不是新鮮事了,就算沒有一級函式的特性,不少語言還是內建有微語言,像是規則表示式(Regular expression),儘管能力有限,規則表示式確實是一種語言,主要用來進行字串匹配的語言!
一級函式的規則本身就是微語言!若要再精確一些,一級函式代表著某種形式的 lambda 表示式,lambda 表示式就是個微語言,要建立 lambda 表示式,最簡形式只有三條語法規則:
x
:變數(Variable),名稱可以是單一字元(像是x
)或字串(像是map
),用來代表參數或數學/邏輯值(值也是個 lambda 表示式)。(λx.M)
:抽象(Abstraction),也就是函式定義,x
被綁定在表示式中,M
會是個 lambda 表示式。(M N)
:套用(Application):M
、N
是 lambda 表示式(N
被當成是M
的引數)。
顯然,lambda 表示式就是運算表示式,對照至 JavaScript 箭號函式:
x
:變數。x => M
:抽象。M(N)
:套用。
在之前的文件中,也已經看到了,可以將 JavaScript 程式碼完全使用箭號函式表示,JavaScript 執行環境完全可以理解箭號函式,因此也可以執行轉換出來的箭號函式,這只是方便驗證這一路轉換出來的箭號函式是否正確罷了。
既然 JavaScript 箭號函式,可以完全對應 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))
也不是不行,只不過會得到冗長(且包含岐義) 的句子罷了(在之前的文件中也看到了,就連 1、+
、-
這類描述,也可以形式化)。
既然任何語言撰寫的程式,都可以使用 lambda 表示式來表達,那麼定義 lambda 表示式規則的 lambda 演算,確確實實就是個語言,lambda 表示式就是運算表示式,既然任何語言撰寫的程式,都可以使用 lambda 表示式來表達,那麼從 lamdbda 演算的角度來看,程式就是個規模大或小的運算式罷了。
實際上,使用任何程式語言,針對需求來撰寫程式,就是在使用該語言的規則,將直覺描述的需求進行形式化的過程,只不過,現代使用的多半是抽象程式高的高階語言,使用的是 if
、while
、for
這類更接近人類描述的語法,因而掩蓋了運算的本質,甚至有不少人將運算與程式分開看待。
從 lambda 演算的角度來看,程式就是規模大或小的運算式罷了,也就能進行運算,也就是說,lambda 演算清晰地定義什麼是可運算函式,什麼問題是可運算的,然而這也暗示著,相對的另一端存在著不可運算的問題、不可運算的函式…