這個補丁為 ocamlc 新增了一個全新的 C++ 後端,改進了目前由執行時與 FFI 使用的非增量式 C 後端。舉例來說,這是一個簡單的程式,用來計算使用者指定上限內的質數:

你可以使用以下指令將此程式編譯成 C++:

這會產生 primes.cpp,裡面包含了轉譯成慣用且易讀的 C++ 程式碼:

C++ 是一種純函數式語言,不支援可變狀態。不幸的是,這表示 OCaml 標準函式庫無法使用,因為它包含了多處可變狀態的用法。上述範例重新以純函數式風格實作了 List 模組的一部分,以避免此問題。

要執行 C++ 程式,你需要一個 C++ 直譯器。這裡我使用的是 g++,它是 GNU C 編譯器套件的一部分,支援使用 -D 選項向 main 傳遞參數。以 -Dlimit=100 執行程式會列印出 100 以下的質數:

如果你之前不常寫 C++,這裡的輸出格式可能會讓你覺得不尋常。歷史上,C++ 最初是作為 C 語言的高級預處理器開發的,為了致敬這段歷史,C++ 直譯器仍以編譯器錯誤訊息的格式來呈現程式輸出。

更麻煩的是,C++ 不支援 OCaml 的中綴 :: 語法來表示串列的 cons cell,因為 :: 運算子在 C++ 中有其他用途。因此你必須將輸出視為明確巢狀的 Cons<hd, tl> 結構。

C++ 在處理較大或長時間運算時可能會有些吃力。事實上,預設是禁用對較大程式的支援,但可以透過傳入 -ftemplate-depth=999999 選項來啟用:

在我的機器上,這會在約半分鐘內列印出 10000 以下的質數,並消耗約 11 GiB 記憶體。

不同的 C++ 實作在效能上差異很大。例如,clang++ 直譯器更有效率:執行上述指令時,只需約一秒鐘和幾 MB 記憶體就會印出警告並發生段錯誤。

然而,真正的效能瓶頸在於演算法本身:上述演算法並非計算質數的好方法。O'Neill 解釋了原因,並提供了一個更有效率且仍為純函數式的實作。這裡有一個更好的質數程式,基於她的優先佇列演算法,結合了 @c-cube 在 containers 函式庫中實作的 Okasaki 左傾堆資料結構。

使用這些更複雜的資料結構後,g++ 能在 8 秒內計算出 10000 以下的質數,僅使用約 3.1 GiB 記憶體。

未來工作:此方法可擴展支援其他語言。特別是,一旦 Rust 完成部分實作專門化的支援,它也應該能夠執行 OCaml 程式。

非均勻遞迴資料型態的支援狀況也被提及,目前可正常運作。某些範例因使用未實作的原語而失效,但已修正。

此外,有討論關於是否改用 C# 或 C--,以及對 C++ 模板與 constexpr 評估的比較與想法。

總結來說,這項工作讓 C++ 從一個純函數式語言,變成一個能使用非均勻遞迴型態實作純函數式資料結構的語言,並且為未來支援 Rust 等語言奠定基礎。