誰是微語言?

December 15, 2021

在為各種資料編碼的過程中,漸漸感覺到一個微型語言正在成形,因為 JavaScript 提供箭號函式,令這微語言可以在 JavaScript 的環境中執行,也就是說,在 JavaScript 語言中創造了另一門語言!?

構造語言?

從語言中提供有一級函式特性的角度來看,確實是有這樣的可能性存在,現今不少具有一級函式特性的語言,也會以此為亮點,像是可以基於這些語言,來構造專屬的領域特定語言(Domain Specific Language, DSL),像是 Scala、Ruby 等就是如此。

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

若是如此,先前文件中建立的各種資料編碼、yesnowhen 等,就像是 JavaScript 建立起來的…程式庫、框架、API…你要怎麼稱呼都行…這麼看來,那些像是原生語言語法的 DSL,說穿了也一樣,就是程式庫、框架、API…只不過用這些名詞看來有點 Low,就取了較有亮點 DSL 罷了…

真正的微語言,其實是「lambda 表示式」!

JavaScript 一開始就有一級函式,也就是說,JavaScript 早就內建了一個微語言在自身之中,只不過,在 ES6 提供箭號函式,function 這關鍵字太長了,用它來談之前的文件,眼睛會花的更嚴重!不過,真要用還是可以的!

語言中內建微語言早就不是新鮮事了,就算沒有一級函式的特性,不少語言還是內建有微語言,像是規則表示式(Regular expression),儘管能力有限,規則表示式確實是一種語言,主要用來進行字串匹配的語言!

一級函式的規則本身就是微語言!若要再精確一些,一級函式代表著某種形式的 lambda 表示式,lambda 表示式就是個微語言。

lambda 表示式

要建立 lambda 表示式,最簡形式只有三條語法規則:

  • x:變數(Variable),名稱可以是單一字元(像是 x)或字串(像是 map),用來代表參數或數學/邏輯值(值也是個 lambda 表示式)。
  • (λx.M):抽象(Abstraction),也就是函式定義,x 被綁定在表示式中,M 會是個 lambda 表示式。
  • (M N):套用(Application):MN 是 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 演算的角度來看,程式就是個規模大或小的運算式罷了。

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

從 lambda 演算的角度來看,程式就是規模大或小的運算式罷了,也就能進行運算,也就是說,lambda 演算清晰地定義什麼是可運算函式,什麼問題是可運算的,然而這也暗示著,相對的另一端存在著不可運算的問題、不可運算的函式…