Lambda Prelude は柔軟な反復開発と本番デプロイを分離する。インタプリタは AST をツリーウォークして素早くフィードバックを返し、build は Lambda Prelude のソースを OCaml ソースに変換して ocamlopt に渡し、真のネイティブバイナリを出力する。
独自のマシンコードバックエンドもバイトコード VM も持たない。コンパイラは Lambda Prelude の構文 / 型 / アクターモデルを OCaml の型 / 関数 / レコード / クロージャ / effect にマップする。コード生成・最適化・リンク・バイナリ配置は OCaml ツールチェーンに委ねる。
パイプライン
.lp ソース
├── lexer / parser
├── 型推論 (プロトコル付き HM、行多相)
├── AOT 変換 (AST → OCaml ソース)
├── ocamlopt (ネイティブコード生成)
└── リンカ (AOT ランタイム)
.exe
言語実装は Lambda Prelude 固有の意味論 (型 / プロトコル / ディスパッチ / アクタープリミティブ) を担当する。バイナリ生成は成熟した OCaml 側に任せる。
lambda-prelude build script.lp --out binary
lambda-prelude build script.lp --emit # 生成された OCaml を標準出力に
lambda-prelude build script.lp --out script.ml # 生成された OCaml をファイルにAOT バイナリが意味の正典
インタプリタと AOT バイナリの挙動が食い違ったとき、AOT バイナリが正典。インタプリタは開発ツール — 拡張が速く、機能を AOT 経路に組み込む前のプロトタイプに有用 — だが、言語としてコミットするのは AOT 経路。インタプリタが AOT と一致しない挙動を見せたら、それはインタプリタ側のバグとして扱う。
これは以下と同じ系列にある。
- OCaml —
ocamloptが本番実行の基準、toplevel / bytecode は反復開発や軽量実行向け - Haskell — GHC が正典、GHCi は REPL
- Rust —
rustcが正典、MIRI は実験用解釈系
逆の伝統 — インタプリタを正典実行モードとする — は Python / Ruby / Lua のような動的型言語、およびイメージベース Smalltalk のもの。Lambda Prelude は表層こそ Smalltalk 風だが、本体は静的型付きで AOT ネイティブ寄りなので、静的 / コンパイル系列に並ぶ。
バイト単位の退行テスト: ほとんどのサンプルプログラムはインタプリタと AOT バイナリ両方で実行され、出力の食い違いはハーネスが拒否する。
生成されるバイナリ
lambda-prelude build はネイティブ実行ファイルを出力する。LP ソースを OCaml ソースへ変換し、ocamlopt でネイティブコンパイルする。生成バイナリには LP のランタイム(値表現・アクタースケジューラ・組込メソッド・メソッドディスパッチ経路)が一緒にコンパイルされる。静的にレシーバが特定できる送信は直接の関数呼び出しになり、特定できない送信(リフレクション / リモート / プラグインクラス)だけが実行時のメソッドディスパッチを通る。
「完全静的」専用のビルドモードも退避用の切り替えも存在しない。各呼び出しサイトは利用可能な最良の形に展開され、両種が同一バイナリ内に共存する。
ディスパッチの 4 カテゴリ
Lambda Prelude は静的型付きだが、すべての送信がコンパイル時解決を要求されるわけではない。設計上 4 つのカテゴリが共存する — 1〜3 は言語がコミットする意味境界、4 は推論の強化とともに縮んでいくエンジニアリング上の状態。
- 静的ディスパッチ。レシーバがコンパイル時に特定でき、直接 OCaml 呼び出しに展開される。最も多いケース。Hindley-Milner 推論はプロトコル制約付き多相とアクター行多相をカバーし、型注釈は任意。
- リフレクション。
perform:、Reflect、className、atField:put:はセレクタ / クラス名を実行時に扱うので、常に動的。 - 分散。
Remote at:for:id:はプロキシを返す。プロキシへの送信はワイヤ越しにシリアライズされ、リモート側はふつうの Lambda Prelude クラス。Remote at: url for: #Class id:のようにリテラルのクラスを渡すとプロキシは型に乗り、送信は対象クラスのプロトコルに対してコンパイル時に型検査される — 未知セレクタはコンパイルエラー。ただしディスパッチは常にワイヤ越しで、AOT がプロキシ送信を直接のローカル呼び出しに落とすことはない。非リテラルのfor:はコンパイル時にクラスを名指せないので従来通り動的型付けのまま。 - ランタイムディスパッチ。レシーバを推論が特定できない送信 (不透明型上の多相メソッド、プラグインクラス、動的な JSON ペイロード) は、バイナリが
lib/eval.ml経由で link するランタイムディスパッチャに委ねられる。
実行モード対応表
| 機能 | インタプリタ | AOT |
|---|---|---|
| アクター基本 (spawn / send / FIFO receive / mailbox) | あり | あり |
| 選択受信、受信タイムアウト | あり | あり |
Future、ask:、Future タイムアウト | あり | あり |
| リンク / モニタ / 終了シグナル捕捉 / スーパーバイザ | あり | あり |
| 境界つきソケット I/O | あり | あり |
| HTTP サーバ / クライアント | あり | あり |
TLS ソケット (Tls.Engine 駆動) | あり | あり |
| JSON、JSON-RPC、TERIOS RPC (HTTP 経由) | あり | あり |
| SQLite (組み込み)、File / OS / サブプロセス / リフレクション | あり | あり |
| PostgreSQL / MariaDB / MySQL コネクション (Dynlink プラグイン) | あり | あり |
| Redis / Valkey (RESP2 wire、Dynlink プラグイン) | あり | あり |
| TORM 2-way SQL テンプレート + DAO マクロ | あり | あり |
| マルチドメイン spawn、クロスドメイン inbox、ワークスティーリング | あり | あり |
AOT バイナリには同じランタイムが一緒にコンパイルされているので、マルチドメインワークスティーリングスケジューラをそのまま備え、同じ Dynlink プラグインを読み込む。2 列は構造上同一になる。
クロスプラットフォームビルド
出力は Lambda Prelude のソースから直接ビルドされたネイティブバイナリで、コンテナ・Windows 開発環境・Linux デプロイ先のいずれにも適合する。
- Windows — MinGW-w64 リンク、実行時 Cygwin DLL 非依存。Windows は第一級の開発環境。
- Linux — 標準 ELF バイナリ。本番デプロイ先。
機能が完成したと判定する前に両ターゲットを通す。片方でしか動かない機能は未完成扱い。
生成コードの覗き方
--emit は変換後の OCaml ソースを標準出力に書く。--out <file>.ml を渡せばファイルに書き出せる。AOT がコードに対して実際に何をしたかを調べる標準ツール。
lambda-prelude build examples/15_counter_actor.lp --emit | less
lambda-prelude build examples/15_counter_actor.lp --out counter.ml
出力はふつうの OCaml — 型が通り、必要なところに型注釈が付き、平坦な IR ではなく入力ソースの形に近い見た目で並ぶ。