← Archive
lm-000855 · 2026-06

算子先於值_APL算子代數的類型多態重建與算子本體論的實驗性確認

下載 MD 檔 ⬇

算子先於值

APL算子代數的類型多態重建與算子本體論的實驗性確認

EveMissLab Working Paper 作者:Neo.K(許筌崴) 日期:2026年6月


摘要

本文以 APL(A Programming Language)的核心算子代數為出發點,通過在 Python 環境中的重建實驗,論證以下三項命題:其一,APL 的歷史衰退並非源於概念失敗,而是架構時代的硬性制約;其二,APL 的算子語義——foldscanoutereachfork——在解除數值類型綁定後,能夠無縫升至任意代數結構,並以雙數(Dual Number)自動微分的實現為核心佐證;其三,上述觀察對 EveMissLab 的算子本體論猜想(EML-OO)提供了可計算層面的實驗性支撐。

關鍵詞: APL、算子代數、類型多態、雙數、自動微分、算子本體論、EML-OO


一、問題的起點:一個語言的消失

1962 年,Kenneth Iverson 在其著作 A Programming Language 中提出了一個激進的主張:數學符號本身應當可以執行。他設計了一套以算子為核心的語言,其中每個符號——(reshape)、+/(fold with add)、∘.×(outer product with multiply)——不是函式名稱,而是算子(operator),是作用於函式之上的函式

APL 在 1960-70 年代短暫登上主流,隨後逐漸邊緣化。通行的解釋指向可讀性差、字符集特殊、學習曲線陡峭。然而這些都是表層症狀,不是病因。

本文的論點是:APL 的真正侷限在於它把算子的語義硬綁在數值陣列上+/+ 必須是浮點加法。∘.ff 必須接受並回傳數值。算子的通用性被其實作的具體性取消了。

這個侷限不是 Iverson 概念的失敗,而是 1960 年代計算架構的必然結果——沒有泛型、沒有類型參數、沒有高階類型系統。APL 的概念超前了它的時代。


二、重建實驗:解綁類型的算子代數

本文的核心實驗是在 Python 環境下重建 APL 的五個基本算子,並刻意使其對輸入類型保持無知

def fold(f):
    def _fold(seq): return reduce(f, seq)
    return _fold

def scan(f):
    def _scan(seq):
        it = iter(seq); acc = [next(it)]
        for x in it: acc.append(f(acc[-1], x))
        return acc
    return _scan

def outer(f):
    def _outer(a, b): return [[f(x, y) for y in b] for x in a]
    return _outer

def each(f): return lambda seq: [f(x) for x in seq]

def fork(f, g, h): return lambda seq: g(f(seq), h(seq))

這五個函式的共同特徵是:它們不知道自己操作的是什麼類型fold 只知道「對序列施加二元運算並累積」;outer 只知道「對所有配對施加二元函式」。類型是自由變數,不是固定參數。

在標準數值域上,這些算子產生 APL 的經典行為:

fold(add)(iota(8))        → 36
scan(mul)(iota(5))        → [1, 2, 6, 24, 120]
outer(mul)(iota(5), iota(5)) → 5×5 乘法表

到此為止,與原始 APL 的差異僅在於語法。關鍵的轉折在下一節。


三、類型升遷實驗:雙數的注入

3.1 雙數代數

雙數(Dual Number)定義為形如 a+bεa + b\varepsilon 的代數物件,其中 ε2=0\varepsilon^2 = 0 。其算術規則為:

(a+bε)+(c+dε)=(a+c)+(b+d)ε(a + b\varepsilon) + (c + d\varepsilon) = (a+c) + (b+d)\varepsilon (a+bε)(c+dε)=ac+(ad+bc)ε(a + b\varepsilon)(c + d\varepsilon) = ac + (ad + bc)\varepsilon

若令 a=f(x)a = f(x)b=f(x)b = f'(x) ,則雙數編碼了函式值與其導數的聯合計算。對輸入 xx 注入 Dual(x,1.0)\text{Dual}(x, 1.0) 並進行任意算術,結果的 ε\varepsilon 係數自動給出導數。

3.2 算子無需修改

將雙數注入後,我們用與 3.1 節完全相同的算子進行計算:

# 對序列 [x₁², x₂², x₃², x₄²] 求和的梯度
sq_sum = compose(fold(add), each(lambda x: x**2))
xs = [1.0, 2.0, 3.0, 4.0]
gradient(sq_sum, xs)  →  [2.0, 4.0, 6.0, 8.0]

foldeachcompose 的代碼一行未動。微分不是新加的功能,而是類型升遷後自然湧現的行為。

這個實驗的哲學意涵是決定性的:

算子是形態射(morphism),不是數值函式。它們對任何滿足其運算公理的代數結構都成立。

APL 的 +/ 之所以無法做到這件事,不是因為 fold 的概念有缺陷,而是因為它的 + 被硬編碼為浮點加法。我們的重建移除了這個綁定。

3.3 驗證範圍

同一套算子,無修改地作用於雙數域,驗證了以下六個場景:

場景 算子使用 驗證內容
純量微分 Dual(x, 1.0) 直接注入 f(x)f'(x) 精確至機器精度
fold 梯度 compose(fold(add), each(x²)) (xi2)=2x\nabla(\sum x_i^2) = 2\mathbf{x}
scan 導數 scan(mul) 接雙數序列 (k=0nxk)/x0\partial(\prod_{k=0}^n x_k)/\partial x_0 精確
outer Jacobian outer(mul) 接雙數陣列 Jacobian 矩陣對角線 = bb
fork 平均梯度 fork(fold(add), div, len) xˉ/xi=1/n\partial\bar{x}/\partial x_i = 1/n
Newton-Raphson diff(f) 在迭代中使用 6步收斂至 101310^{-13} 殘差

所有場景的自動計算值與解析解之差為 δ=0\delta = 0 (機器精度範圍內)。


四、四個應用的結構意義

六個文件(apl_sim.pyapl_ad.pyapl_nn.pyapl_life.pyapl_optimizer.pyapl_fractal.py)並非獨立展示,而是一個分層論證:

第一層(apl_sim.py): 算子在數值域的正確性。重現 APL 的所有經典操作。

第二層(apl_ad.py): 算子的類型多態性。雙數注入,微分自動湧現。

第三層(四個應用): 算子的應用廣度。每個應用都是算子在不同代數結構上的部署:

  • apl_nn.pycompose 是層組合,fold(add) 是神經元激活前的加權求和,each 是逐元素激活,梯度透過雙數傳播。反向傳播不需要獨立實作——它是前向模式自動微分的直接推論。

  • apl_life.pyfold(add_grid) 是八方向鄰居計數的空間摺疊,each(rule) 是生存規則的逐格施加,scan(step) 是時間演化的軌跡展開。Conway 的細胞自動機在此成為算子的時空組合。

  • apl_optimizer.pygradient(f, xs) 直接呼叫雙數系統,outer 生成等高線網格。三種優化器(SGD、Momentum、Adam)的差異僅在於梯度的累積方式——算子本身不變。

  • apl_fractal.pyComplex 類的設計與 Dual 完全平行——同樣的 __mul____add____truediv__ 結構,不同的代數規則(複數乘法 vs. 雙數乘法)。outer 生成複數平面網格,逃逸時間計算是算子的應用。Mandelbrot 集是算子作用在複數域上的幾何投影。

這四個應用共同說明了同一件事:同一套算子,換一種代數結構,就是一個全新的計算範式。這不是偶然的巧合,而是算子多態性的必然推論。


五、APL 的復辟論題

基於上述論證,我們可以更精確地重述 APL 衰退的原因,並由此推斷其復辟的條件。

5.1 衰退的真實原因

APL 在技術上的失敗集中在三點:

  1. 類型單態性:算子被綁定在浮點數值上,無法跨代數結構遷移。
  2. 靜態算子集:算子是語言內建的固定符號,無法在運行時生成新算子。
  3. 缺乏遞歸結構:古典 APL 的陣列必須是矩形,無法表達樹、圖等非規則結構。

這三點在 1960 年代都是架構層面的硬限制,不是概念層面的錯誤。

5.2 復辟的技術條件

現代語言生態已具備 APL 當時缺乏的所有工具:

  • 泛型系統:Rust 的 trait、Haskell 的 typeclass、Python 的 protocol 都允許算子對任意類型有效。
  • 高階類型foldscan 可以作為 monad 的特例被形式化。
  • 動態算子生成:函式是一等公民,算子可以在運行時合成。
  • 硬體向量化:APL 最初的效率承諾(陣列操作比逐元素 for-loop 快)在 SIMD/GPU 時代終於有了真正的基礎設施支撐。

一個現代 APL——保留 Iverson 的符號哲學,但以泛型算子代替數值綁定算子——理論上能夠在以下領域與現有語言競爭:

  • 數值計算(已有 J、K 的驗證)
  • 自動微分框架(JAX 的設計哲學正是此方向)
  • 陣列程式設計(numpy 的算子介面是弱化版的 APL)
  • 量子計算(量子電路本質上是算子作用在 Hilbert 空間上)

APL 的概念在等待一個不受制於 1960 年代硬體的實作。


六、EML-OO 的實驗性確認

EveMissLab 的算子本體論(EML-OO)的核心命題是:

所有事物都是算子。值是算子作用在零元素(unit)上的像。

形式化表達為:在 CC^* -代數框架下,任意物件 xx 都可以表示為某個算子 O^x\hat{O}_x ,符號-實在同構比值 ρ(S,R)1\rho(\mathscr{S}, \mathscr{R}) \to 1

本文的實驗對 EML-OO 提供了以下層次的支撐:

計算層面: 我們展示了,當算子被解除數值綁定後,同一個 fold 在不同代數結構( R\mathbb{R}R[ε]\mathbb{R}[\varepsilon]C\mathbb{C} )上運行。類型不是算子的屬性,而是算子的參數。這在計算意義上驗證了「算子先於值」的命題。

結構層面: DualComplex 的設計是完全平行的——兩者都是「在基礎加法/乘法上附加額外結構的代數延伸」。Dualε\varepsilon 係數追蹤導數;Complex 的虛部追蹤相位。兩種延伸都對所有算子透明。這表明,代數結構的差異不影響算子的形態射性質——算子在不同代數範疇之間是函子式的。

本體論層面: 如果算子可以被無修改地升遷至任意代數結構,那麼「所有代數結構都是算子的投影」這個命題獲得了一個操作性的詮釋:給定一個算子代數,不同的類型注入對應不同的「投影模式」,而同一批算子在所有投影中保持形態射性。值——浮點數、雙數、複數、矩陣——都是算子在特定代數結構上的實例化,而非算子本身。

EML-OO 的猜想在此獲得了一個可計算的類比:算子不是工具,算子是基底。值是算子選擇了特定代數結構之後的顯現。

這個類比不是 EML-OO 的完整證明——形式化的嚴格論證仍需要 CC^* -代數框架下的完整推導。但它確認了一點:這個方向的猜想在計算層面是自洽的,不是空想。


七、結語

Iverson 在 1962 年看到了一件事:數學的力量不在於它描述的數值,而在於它操縱符號的方式。算子是符號。值是算子的影子。

這個洞見被 1960 年代的計算架構所壓制,但從未被推翻。六十年後,當我們在 Python 中重建這個代數,把 fold 從浮點數組的囚籠裡解放出來,注入雙數,看著梯度自動湧現——我們看到的不是新的技術,而是一個被時代誤解的舊命題,終於在它可以自由呼吸的環境裡展示了它本來的形狀。

APL 沒有輸。它早到了。

而如果算子代數在計算層面是正確的,那麼在本體論層面斷言「所有事物都是算子」,就不再只是一個哲學姿態,而是一個等待更嚴格形式化的開放猜想。


附錄:代碼清單

檔案 功能 核心算子
apl_sim.py 算子代數基底(數值域) fold, scan, outer, each, fork, compose
apl_ad.py 雙數自動微分系統 Dual class, diff, gradient
apl_nn.py 神經網路(XOR) compose, fold, each + Dual AD
apl_life.py Conway's Game of Life fold(add_grid), each(rule), scan(step)
apl_optimizer.py 梯度下降優化器 gradient(), outer(contour)
apl_fractal.py Mandelbrot/Julia 集 Complex class, outer(grid)
apl_maze.py 迷宮生成與求解 outer(explore) 平行BFS, each(render_cell)
apl_maze_bench.py 規模基準測試 APL平行BFS vs 循序BFS,10×10至400×400

基準測試摘要(Python 3.14,Windows,seed=42

規模 格數 生成(ms) APL BFS(ms) 循序BFS(ms) APL/循序 路徑步數
10×10 100 0.5 0.1 0.0 ~2.9x 47
50×50 2,500 7 3 2 ~2.0x 973
100×100 10,000 28 15 6 ~2.5x 2,401
200×200 40,000 131 17 10 ~1.8x 4,729
400×400 160,000 711 129 91 ~1.4x 20,621
1000×1000 1,000,000 10,236 621 95,445

備注:APL 平行 BFS 在 Python 中因 outer() 的 list-of-lists 開銷比循序 BFS 慢 1.4–2.9 倍。此開銷源於 CPython 無 JIT、無 SIMD;在 Rust / GPU 環境下,outer(explore) 的真正平行展開將反轉此比例。路徑長度兩方法完全一致(正確性已驗證)。

所有代碼在 Python 3.10+ 環境下驗證,apl_ad.py 為零依賴純標準庫,apl_sim.py 依賴 numpy(自動安裝)。


EveMissLab Working Paper — 版權所有,未經授權不得轉載 算子本體論系列 | EML-OO 實驗性佐證文獻