2020s-函數式思想的全面內建#
案件摘要#
到了 2010 年代末與 2020 年代,函數式思想完成了一件百年運動的終極目標:它不再需要「函數式語言」這個專屬載體——它被內建進所有主流語言。Rust 的 Option/Result 與 iterator、Swift 的 enum 加 pattern matching、TypeScript 的 discriminated union、Kotlin 的 data class、Java 的 Stream(2014)、C++20 的 ranges、Scala 3(2021),全是 λ-Calculus 的後裔。與此同時,真正的函數式語言也攻入工業要塞:Clojure 進入拉丁美洲最大數位銀行 Nubank(2021 年收購 Cognitect)、Haskell 進入區塊鏈 Cardano/Plutus、OCaml 進入 Tezos/Michelson、Futhark(2017)把純函數式程式編譯到 GPU。本案要偵破的謎題是:為什麼是「不可變」與「純函數」這兩個看似高昂的代價,贏下了多核與平行時代?
前因 -- 為什麼會有這個案子#
- 2005 年前後,摩爾定律的頻率紅利見頂,處理器轉向「加核數」:多核(multicore)與眾核(many-core)成為常態,單執行緒效能不再免費成長。
- 共享可變狀態成為效能與正確性的共同敵人:鎖(lock)既拖慢速度又引入死鎖;data race 是不可重現的 bug,除錯成本無上限。
- 非同步回呼地獄(callback hell):Node.js 與瀏覽器的事件迴圈中,巢狀回呼層層堆疊,控制流與錯誤處理支離破碎。
- 兩個陣營的實證判決:Haskell 與 Rust 用 immutability 與型別安全證明——「不可變」不只更安全,在平行場景還更快,因為不需要鎖。
- 學院理論早已備齊:Chris Okasaki 1998 年的《Purely Functional Data Structures》證明持久化資料結構可以高效;線性型別(linear types)在 1987 年由 Girard 提出,等待被工程化。
線索與推理 -- 數學式、程式、理論#
線索一:Option/Result monad 的內建——? 就是 bind#
Rust 把 Maybe monad 直接做成標準庫型別:
傳統函數式語言用 >>=(bind)鏈式傳播 Maybe:
Rust 用一個 ? 運算子做同一件事:遇到 None 或 Err 就提前返回,否則取出值繼續。語法糖底下是同一個 monad 定律:
fn parse_config(path: &str) -> Result<u32, String> {
let text = std::fs::read_to_string(path)?; // Err 就提前返回
let line = text.lines().next()
.ok_or("empty file".to_string())?; // None 轉 Err 再傳播
let value = line.trim().parse::<u32>()
.map_err(|e| e.to_string())?; // bind 的語法糖
Ok(value)
}
// 每一行 ? 都是 >>=:鏈式錯誤傳播,錯誤路徑一目了然
Haskell 的 do notation、Rust 的 ?、Swift 的 try,全是同一個 monad 在不同語言裡的內建化身——錯誤處理從「散落的 if 檢查」收斂成「型別與運算子的代數」。
線索二:持久化資料結構與結構共享#
不可變聽起來要每次複製整份資料,代價 O(n)。Okasaki 的持久化資料結構(persistent data structure)用結構共享(structural sharing)破解:修改時只複製路徑上 O(log n) 個節點,其餘節點與舊版本共享。
以 persistent vector(Clojure 的核心結構,基於 32 分叉樹)為例:
用 Python 模擬這個結構共享思想:
class Node:
def __init__(self, children):
self.children = children # 不可變:建好後不再修改
def update(node, path, value):
# 沿路徑只複製 O(log n) 個節點,其餘與舊版共享
if not path:
return value
i = path[0]
new_children = list(node.children) # 淺複製:引用共享
new_children[i] = update(new_children[i], path[1:], value)
return Node(new_children)
# 32-ary tree,內含 3 層:root -> branch -> leaf
leaf_a = Node([1, 2, 3])
leaf_b = Node([4, 5, 6])
branch = Node([leaf_a, leaf_b])
root_v1 = Node([branch])
root_v2 = update(root_v1, [0, 0, 1], 99) # 改 leaf_a 的第 2 個元素
print(root_v2.children[0].children[1] is root_v1.children[0].children[1]) # True:leaf_b 完全共享
print(root_v2.children[0].children[0].children[1]) # 99:新路徑
print(root_v1.children[0].children[0].children[1]) # 2:舊版不受影響
兩個版本並存(持久性)、修改只花 O(log n)、未動過的子樹共享記憶體——不可變的代價被數學壓到近於零,這是 copy-on-write 快取友善與無鎖平行的基礎。
線索三:ownership 是線性型別的工程化#
Rust 的 ownership + borrowing 是「線性型別」lambda calculus 的變體:每個值恰好有一個 owner(線性使用),借用(borrow)分為可變(&mut,獨占)與不可變(&,共享)兩種,且互斥。這正是 Girard 線性邏輯的工程判決:
資料競爭(data race)在編譯期就被型別系統拒絕:兩個執行緒不能同時持有同一資料的可變借用。「共享可變狀態」這個敵人,被線性型別從語言層面消滅——不需要執行期鎖,也不需要垃圾回收。
線索四:async/await 是 continuation 的語法糖#
回呼地獄的解藥 async/await(JavaScript 2017、Rust 2019、Python 2015),其數學本源是 continuation passing style(CPS):每個 await 點把剩下的計算打包成 continuation,交給事件迴圈在未來恢復。
巢狀回呼被壓平成線性控制流,錯誤處理回歸 ?/try/catch 的代數。控制流不見了嗎?不——它被 monad(Future/Promise)與 continuation 收進型別裡。
線索五:GPU 上的純函數式——Futhark#
Futhark(2017,DIKU)把純函數式程式編譯到 GPU。GPU 有數千核心,共享可變狀態的鎖在 GPU 上根本不可行;但純函數無副作用,平行化是自動的:
純度決定了可平行性:無副作用 ⇒ 無資料競爭 ⇒ 自由平行。這是函數式思想最純粹的兌現場合。
結案 -- 後果與影響#
- λ-Calculus 誕生九十年後(1936→2026),成為三大前沿的底層語法:
- 機器學習:Google 的 JAX 用函數式轉換
jit/vmap/grad——只有純函數才能自動微分與安全編譯,PyTorch 的 functorch 亦同源。 - 區塊鏈:Cardano 的 Plutus 用 Haskell、Tezos 的 Michelson 源自 OCaml——智能合約因為資產不可逆,必須形式化驗證,純函數語言是唯一可信的載體。
- 平行運算:Futhark 到 GPU、Rust 在 Linux 核心與 Windows 的採用,證明無鎖平行的工業價值。
- 函數式思想全面內建:Rust/Swift/TypeScript/Kotlin/Java Stream/C++20 ranges/Scala 3——「先學 Haskell 再用主流語言」變成工程師的實際建議。
- Nubank(2021 年收購 Clojure 母公司 Cognitect)以 Clojure 支撐數千萬客戶的銀行系統,函數式語言正式進入一線金融。
- 「以函數為萬物本源」的百年運動,自 Church 1936 年的 λ-Calculus、Backus 1977 年的圖靈獎演說、Hudak 1989 年的 Haskell 報告,到 2020 年代的全面內建,以全面勝利收案。
關鍵人物與文獻(條列)#
- Alonzo Church——λ-Calculus 原作者;Alonzo Church, "An unsolvable problem of elementary number theory", American Journal of Mathematics 58, 1936.
- John Backus, "Can Programming Be Liberated from the von Neumann Style?", ACM Turing Award Lecture, Communications of the ACM 21(8), 1978.
- Chris Okasaki, "Purely Functional Data Structures", Cambridge University Press, 1998——持久化資料結構與結構共享的理論基石。
- Jean-Yves Girard, "Linear Logic", Theoretical Computer Science 50, 1987——Rust ownership 的型別理論源頭。
- Graydon Hoare 與 Rust 團隊, "The Rust Programming Language", 2015, https://doc.rust-lang.org/book;Ralf Jung et al., "RustBelt: Securing Foundations for the Rust Programming Language", POPL 2017.
- Troels Henriksen et al., "Futhark: Purely Functional GPU-Programming with Nested Parallelism and In-Place Array Updates", ICL 2017.
- Evan Czaplicki 與 Clojure/Rich Hickey 譜系:Rich Hickey, "Clojure", 2007, https://clojure.org;Nubank 收購 Cognitect, 2021.
- Jean-Marc Eber 與 Tezos 譜系:OCaml 於 INRIA(Xavier Leroy et al., "The OCaml System", 1996–);Tezos 的 Michelson 合約語言, https://tezos.com.
- Simon Marlow et al., "Haskell 2010 Language Report", 2010;Cardano 的 Plutus, 2021, https://plutus.cardano.intersectmbo.org.