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

**EveMissLab Working Paper**
**作者：Neo.K（許筌崴）**
**日期：2026年6月**

---

## 摘要

本文以 APL（A Programming Language）的核心算子代數為出發點，通過在 Python 環境中的重建實驗，論證以下三項命題：其一，APL 的歷史衰退並非源於概念失敗，而是架構時代的硬性制約；其二，APL 的算子語義——`fold`、`scan`、`outer`、`each`、`fork`——在解除數值類型綁定後，能夠無縫升至任意代數結構，並以雙數（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 的真正侷限在於它把算子的語義**硬綁在數值陣列上**。`+/` 的 `+` 必須是浮點加法。`∘.f` 的 `f` 必須接受並回傳數值。算子的通用性被其實作的具體性取消了。

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

---

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

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

```python
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\varepsilon$ 的代數物件，其中 $\varepsilon^2 = 0$。其算術規則為：

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

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

### 3.2 算子無需修改

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

```python
# 對序列 [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]
```

`fold`、`each`、`compose` 的代碼一行未動。微分不是新加的功能，而是類型升遷後**自然湧現**的行為。

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

> **算子是形態射（morphism），不是數值函式。它們對任何滿足其運算公理的代數結構都成立。**

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

### 3.3 驗證範圍

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

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

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

---

## 四、四個應用的結構意義

六個文件（`apl_sim.py`、`apl_ad.py`、`apl_nn.py`、`apl_life.py`、`apl_optimizer.py`、`apl_fractal.py`）並非獨立展示，而是一個分層論證：

**第一層（apl_sim.py）：** 算子在數值域的正確性。重現 APL 的所有經典操作。

**第二層（apl_ad.py）：** 算子的類型多態性。雙數注入，微分自動湧現。

**第三層（四個應用）：** 算子的應用廣度。每個應用都是算子在不同代數結構上的部署：

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

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

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

- **apl_fractal.py**：`Complex` 類的設計與 `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 都允許算子對任意類型有效。
- **高階類型**：`fold`、`scan` 可以作為 monad 的特例被形式化。
- **動態算子生成**：函式是一等公民，算子可以在運行時合成。
- **硬體向量化**：APL 最初的效率承諾（陣列操作比逐元素 for-loop 快）在 SIMD/GPU 時代終於有了真正的基礎設施支撐。

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

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

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

---

## 六、EML-OO 的實驗性確認

EveMissLab 的算子本體論（EML-OO）的核心命題是：

> **所有事物都是算子。值是算子作用在零元素（unit）上的像。**

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

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

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

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

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

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

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

---

## 七、結語

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 實驗性佐證文獻*
