Phel v0.36.0:將數值塔與一等公變量引入 PHP

Phel 是一種受 Clojure 影響的 Lisp,編譯成 PHP,已發布 0.36.0 版。此更新在語言的精確度與彈性上有重大躍進,加入了強大的數值塔與一等 Var 處理器。對於希望將 Lisp 的表達力與 PHP 生態系的普及度結合的開發者而言,這些變更提供了高精度算術與進階元程式設計的關鍵工具。

數值塔:精確度與任意尺度

在 v0.36.0 中最重要的新增之一是實作了「數值塔」,讓 Phel 能夠處理遠超過標準 PHP 浮點數與整數的精確度。

有理數與大整數

  • 精確有理數: Phel 現在支援比例字面值(例如 1/2-3/4),會直接解析為 Rational 類型。這消除了分數在浮點除法中固有的四捨五入誤差。
  • 大整數支援: 語言現在加入了 BigInteger 以支援任意精度的整數。值得注意的是,PHP 整數在算術運算(+-***)發生溢位時會自動提升為 BigInteger,確保計算不會悄悄地環繞或失去精度。
  • 大十進位數(BigDecimal): 為了固定精度的十進位算術,現在提供 BigDecimal,使用 M 後綴字面值(例如 1.5M)。

算術行為變更

為了支援此塔,幾個核心行為已經改變。最顯著的是,(/ int int) 若結果不是整數,現在會回傳 Rational,而不是預設為浮點數。需要浮點除法的開發者應明確使用 (double ...),或確保其中一個運算元是浮點數(例如 (/ 1.0 2))。

一等 Var 與動態綁定

0.36.0 版引入了一等 Var 處理器,超越了單純的符號解析,讓開發者能以物件方式操作變數本身。

新的 Var 功能

  • Var 處理器: 新的 (var sym) 特殊形式與 #'sym 讀取宏允許建立與操作 Var 處理器。
  • 動態重新綁定: 使用 with-redefswith-bindings,開發者現在可以從映射重新綁定動態 var,系統會確保即使發生例外,原始值也會被還原。
  • 可觀測性: PhelVarStateRegistry 現在支援「監聽」,開發者可使用 add-watchremove-watchVar 透過 alter-var-root 被變更時觸發動作。

擴充的型別系統與集合

除了數字與 var,Phel 也擴充了原生值型別,以更好地符合現代應用需求:

  • UUID: 透過 #uuid "..."random-uuidparse-uuid 原生支援 UUID。
  • 持久佇列(PersistentQueue): 新的 PersistentQueue 提供 FIFO 結構,對 pushpeekpop 操作具備攤銷 O(1) 效能。
  • PHP 整合: PhpClass 值型別現在包裝正規化的 PHP 類別/介面全限定名稱(FQN),簡化 Lisp 與 PHP 之間的橋接。
  • MapEntry: MapEntry 型別現在在值上等同於 2 元素向量,提升雜湊映射迭代的使用便利性。

效能與工具改進

此版本的效能提升主要聚焦於啟動過程中的開發者體驗(DX):

  • 更快的啟動時間: REPL 與測試執行器現在在掃描時會剔除常見的大型目錄,如 vendor/.git/node_modules/,且目錄掃描會在每個程序中記憶化。
  • 測試最佳化: phel test 指令現在加入了強大的新旗標:--list 用於在不執行測試的情況下列出測試,--last-failed 用於針對回歸測試迭代,--slowest=N 用於找出測試套件中的效能瓶頸。
  • 命名空間標準化: 核心函式庫已改為在 (use ...) 陳述式中使用點分隔的類別全限定名稱(FQN),提供更一致的語法於整個生態系。

破壞性變更摘要

升級至 v0.36.0 的使用者應留意以下幾項破壞性變更:

  1. 除法: 當結果不是整數時,(/ int int) 現在回傳 Rational 而非 float
  2. UUID: uuid? 現在嚴格只接受 Phel\Lang\Uuid 物件,而不再接受原始字串。
  3. 綁定: binding 函式現在對非動態 var 拋出 InvalidArgumentException;應改用 with-redefs

Sources