3.1 1985 Miranda 與純度、副作用問題#
前言:惰性社群的十年醞釀#
1977 年 Backus 演說後,英國學術界出現一整批實驗性函數式語言:Turner 的 SASL(1976)與 KRC(1981)率先採用惰性求值,Henderson 的 LML、Johnsson 與 Augustsson 的 LML/Haskell 前身各據一方。David Turner 在 Kent 大學把這些想法冶於一爐,1985 年推出 Miranda——首個被商業支援、用於正式教學的純函數式惰性語言。
Miranda 的歷史地位是「收斂點」:它把此前十年分散的想法——純度、惰性、代數資料型別、模式匹配、高階函數——整合成一個內聚、可教的整體。三年內英國約 200 所大學採用。但 Miranda 是 Research Software Ltd 的商業授權產品,不能自由實作——這個限制直接引爆了 1987 年的 Haskell 委員會(3.2 節)。
核心問題:副作用為什麼是萬惡之源#
命令式程式的困難來自副作用(side effect):函數除了回傳值還修改外部狀態。純函數的定義:
引用透明使等式推理成立,而這正是 Backus 演說承諾的代數性。具體的實務紅利:
- 任何子表達式可被其值替換(反之亦然)→ 編譯器可自由重排、共享、消去計算;
- 測試即代數:純函數的測試不需要 mock 狀態;
- 平行化免費:無共享可變狀態即無資料競爭。
副作用還有兩個更隱蔽的破壞:求值順序變成語義的一部分(先印後算與先算後印結果不同),以及除錯時狀態的時間依賴。```text 命令式(有副作用): let a = f(3) + f(3) -- log = [3, 3] let b = f(3) * 2 -- log = [3, 3, 3] a == b(值相同)但對 log 而言兩者「做了不同的事」 ⟹ f(3) + f(3) ≠ 2·f(3)(代數分配律失效!)
純函數式(引用透明): f(3) + f(3) = 2·f(3) -- 共享子表達式可自由消除/重排 ⟹ 編譯器可做公共子表達式消除、融合(fusion)、平行化
Miranda 的激進答案是:乾脆禁止——沒有賦值、沒有可變變數、IO 只以最受限的方式提供。**純度不是性能犧牲,而是把「時間」從語義中移除。**
## Miranda 的語言形貌
```haskell
-- Miranda 語法(Haskell 的直系祖先)
sieve (p : xs) = p : sieve [x | x <- xs; x mod p > 0]
primes = sieve [2..]
take 5 primes -- [2, 3, 5, 7, 11] —— 無限質數表!
三個特徵值得注意:
- 無限列表
[2..]:只在惰性(3.2 節)下合法; - 列表推導
[x | x <- xs; ...]:Turner 的發明,後成為 Haskell 與 Python comprehensions 的原型; - 衛式(guard)定義:
f x | x > 0 = ...,函數定義像數學方程。
但純度立刻撞上現實:怎麼讀檔案?怎麼印字?Miranda 時代的答案很粗糙——用內建的受限原語或「偽裝成資料的互動」(Turner 的 dialogue 型別,List-of-requests/responses)。這個難題懸置了五年,直到 Haskell 的 Monad(3.3 節)才優雅解決。「純度與 IO 的和解」是 1990 年代函數式語言最重要的理論突破,而它的必要性正是 Miranda 逼出來的。
副作用的代數觀點:什麼被破壞了#
考慮一段共享子表達式:
// JavaScript(副作用)
let log = [];
function f(x) { log.push(x); return x * 2; }
let a = f(3) + f(3); // a = 12,log = [3,3]
let b = f(3) * 2; // b = 12,log = [3,3,3] —— b 與 a 「值相同」但狀態不同!
\(f(3) + f(3) = 2 \cdot f(3)\) 在此不成立(對 log 而言)。乘法分配律等代數定律的失效,使得編譯器不能做公共子表達式消除之外的大多數改寫。這個小小的觀察就是「函數式程式易於最佳化」的全部根據——Haskell 的 GHC 至今仍依賴引用透明做激進的 fusion、inlining 與 strictness analysis。
歷史意義與後續發展#
- Miranda → Haskell(1987-1990): Miranda 的商業授權使學界無法自由擴充與發論文,1987 年 FPCA 會議決議成立委員會設計開放標準語言,Miranda 的幾乎全部設計(語法、ADT、惰性、列表推導)被繼承。
- 純度的制度化:「預設純、副作用用型別標記」成為 Haskell 的核心哲學,也影響了 Scala(ZIO/Cats Effect)、Rust(
&mut獨占借用即一種受控可變性)、Python 社群的純函數風格倡議。 - 教學革命:Miranda 證明「第一次學程式設計可以不學賦值」,影響了之後三十年的課程設計。
Turner 本人的總結可作為本節結語:惰性求值「把程式從時間的暴政中解放出來」——不過先要付的學費,是如何在沒有狀態的世界裡做 IO。這筆帳,Haskell 用一個來自範疇論的概念來還。
重點整理#
- Miranda(1985, Turner)是首個商業支援的純函數式惰性語言,整合了 1976-85 年的實驗成果。
- 副作用破壞引用透明 → 代數定律失效 → 編譯器與程式設計師都失去等式推理。
- 純度的紅利:可替換性、免費平行化、無時間依賴的除錯。
- 列表推導、衛式、無限結構在 Miranda 中定形。
- 純度與 IO 的衝突懸而未決(dialogue 型別過於笨拙),成為 Monad 的動機。
- Miranda 的封閉授權直接導致 1987 年 Haskell 標準化委員會的成立。