跳轉到

1986-StandardML報告#

案件摘要#

1980 年代中期,函數式程式設計的世界出現了一樁「方言叢生」的懸案:1973 年誕生於 Edinburgh LCF 專案的 ML 語言,在各機構衍生出 Edinburgh ML、Poly/ML 等互不相容的版本,程式無法移植。偵探 Robin Milner 於 1984 年提出標準化倡議,並在 1986 年與 Tofte、Harper 聯手完成《The Definition of Standard ML》報告(1990 年正式出版),將 ML 重建為有精確數學定義的主流語言。本案不只是「整理方言」,更開創了兩項偵探技術:以型別推論規則定義靜態語義、以抽象機器定義動態語義——第一個完整做到的主流語言。

前因 -- 為什麼會有這個案子#

  • 1973 年的 ML 原本是 LCF 定理證明器的內部元語言(meta-language),設計時從未考慮成為通用程式語言。
  • ML 隨 LCF 散布到各研究機構後,方言快速分化:Edinburgh ML、Poly/ML 各自修改語法與語義,同一份程式在不同實作上無法執行。
  • 學界與業界需要一個可移植、有正式規格的標準,否則函數式程式設計永遠只是「實驗室玩具」。
  • 同時期的 Hope 語言帶來了代數資料型別(algebraic data type, ADT)與例外(exception)機制,這些好線索需要被整合進標準。
  • Milner 的型別推論(Hindley-Milner, 1978)已在 ML 中實現,但「多型 + 可變參照」的組合被發現會導致型別系統不健全,需要一個正式的修補方案。

線索與推理 -- 數學式、程式、理論#

線索一:模組層次的「函數應用」-- signature 與 functor#

Standard ML 最原創的偵探技術,是把「函數抽象」提升到模組層次。signature 是模組的介面(型別規格),functor 則是「吃進一個結構、吐出另一個結構」的參數化模組——本質上是模組層次的函數應用:

\[ \text{signature } S = \text{sig } \text{spec} \text{ end} \qquad \text{functor } F(X : S) : S' = \text{struct} \ldots \text{end} \]

型別規則上,functor 應用類似項層次的 lambda 應用:

\[ \frac{\Gamma \vdash F : (S, S')\text{-functor} \quad \Gamma \vdash \text{struct} \ldots \text{end} \rhd X : S} {\Gamma \vdash F(\text{struct} \ldots \text{end}) : S'} \]

線索二:靜態語義用推論規則、動態語義用抽象機器#

《The Definition of Standard ML》是第一個用「兩套數學機器」完整定義語言的正式報告。靜態語義以自然演繹風格的型別規則書寫,例如函數應用:

\[ \frac{\Gamma \vdash e_1 : \tau \to \sigma \quad \Gamma \vdash e_2 : \tau} {\Gamma \vdash e_1 \; e_2 : \sigma} \]

動態語義則定義一個抽象機器,以「狀態轉移」描述求值:

\[ (\text{state}) \mapsto (\text{state}') \]

表達式 \(e\) 求值為值 \(v\) 寫作 \(s, e \Rightarrow s', v\) 。這套寫法成為之後所有語言規格的教科書典範。

線索三:value restriction -- 可變參照下的多型危機#

若允許含副作用的表達式自由獲得多型,會出現型別不健全:

\[ \text{let } r = \text{ref } [] \text{ in } r := [1]; \; \text{length}(r) \; \text{(* 期望 [] 卻是 [1] *)} \]

Milner 的破案關鍵:只有「語法上確定為值」的表達式(value restriction)才能一般化(generalize)為多型 schema:

\[ \frac{\Gamma \vdash e : \tau \quad e \text{ 是 value}}{\Gamma \vdash \text{let } x = e \text{ in } \ldots : \forall \alpha.\, \tau} \]

可執行程式碼:SML 的 signature、functor 與 pattern matching#

(* signature:介面 *)
signature ORDERED = sig
  type t
  val compare : t * t -> order
end

(* functor:參數化模組,模組層次的函數應用 *)
functor MakeSet (O : ORDERED) = struct
  datatype set = Empty | Node of set * O.t * set

  fun insert (Empty, x) = Node (Empty, x, Empty)
    | insert (Node (l, y, r), x) =
        case O.compare (x, y) of
          LESS    => Node (insert (l, x), y, r)
        | GREATER => Node (l, y, insert (r, x))
        | EQUAL   => Node (l, y, r)

  fun member (Empty, _) = false
    | member (Node (l, y, r), x) =
        case O.compare (x, y) of
          LESS    => member (l, x)
        | GREATER => member (r, x)
        | EQUAL   => true
end

(* 套用 functor 到具體結構 *)
structure IntSet = MakeSet (struct
  type t = int
  val compare = Int.compare
end)

val s = IntSet.insert (IntSet.insert (IntSet.Empty, 3), 5)
val _ = print (Bool.toString (IntSet.member (s, 5)) ^ "\n")  (* true *)

Python 模擬:functor 的參數化概念#

from typing import Protocol, TypeVar, Generic

class Ordered(Protocol):
    def compare(self, other) -> int: ...

T = TypeVar("T")

class MakeSet(Generic[T]):
    """模擬 functor:吃進一個帶 compare 的結構,吐出集合模組"""
    def __init__(self, cmp):
        self.cmp = cmp
        self.items = []

    def insert(self, x):
        if not self.member(x):
            self.items.append(x)
            self.items.sort(key=self.cmp)

    def member(self, x):
        import bisect
        i = bisect.bisect_left(self.items, x, key=self.cmp)
        return i < len(self.items) and self.items[i] == x

IntSet = MakeSet(cmp=lambda v: v)   # functor 應用
IntSet.insert(3); IntSet.insert(5)
print(IntSet.member(5))             # True

結案 -- 後果與影響#

  • SML/NJ(Standard ML of New Jersey,Appel 團隊,1990 年代)成為編譯器與 GC 研究的標準平台。
  • OCaml(1996)的模組系統(signature、functor)直接源自 Standard ML 的設計。
  • 以推論規則定義靜態語義、以抽象機器定義動態語義的寫法,成為語言規格的教科書典範:Haskell 98 Report、Scala 規格皆效法此模式。
  • value restriction 成為所有 HM 型別系統語言(SML、OCaml、F#)處理可變狀態的標準答案。
  • datatype + pattern matching 的正式語義,確立了 ADT 成為函數式語言的必備配備。
  • 本案證明:一門語言可以像數學定理一樣被完整定義,語言設計從此有了「可驗證的規格文化」。

關鍵人物與文獻#

  • Robin Milner:型別推論(Hindley-Milner)作者、LCF 與 ML 的催生者,1991 年圖靈獎得主。
  • Mads Tofte、Robert Harper:Standard ML 語義定義的共同作者,後續皆成為型別理論與語義學的重要學者。
  • Robin Milner, Mads Tofte, Robert Harper, "The Definition of Standard ML", MIT Press, 1990.
  • Robin Milner, "A Theory of Type Polymorphism in Programming", Journal of Computer and System Systems, 17(3), 1978.
  • Robin Milner, "The Standard ML Core Language", Polymorphism, 2(2), 1985(1984 年提出的標準化草案).
  • Luca Cardelli, "A semantics of multiple inheritance", 1984(Hope/ADT 語義的相關基礎).
  • Andrew W. Appel, David B. MacQueen, "Standard ML of New Jersey", Third International Symposium on Programming Language Implementation and Logic Programming, 1991.