ソースからビルド
Lambda Prelude は OCaml ソースを dune でビルドする。
# 必要環境: OCaml 5.2+ (5.5 で検証) / dune 3.0+ / menhir 2.1+
git clone <repo-url> lambda_prelude
cd lambda_prelude
dune build
Windows では出力バイナリは真のネイティブ MinGW-w64 実行ファイル (実行時 Cygwin DLL 非依存)。ソース開示は NDA ベースのため、本番評価をご検討の際は事前に お問い合わせ ください。
Hello, Prelude
hello.lp を作成する。
'Hello, Lambda Prelude!' printNl.
インタプリタで実行する。
lambda-prelude run hello.lp
ネイティブバイナリにビルドする。
lambda-prelude build hello.lp --out hello
./hello
インタプリタは反復開発用。意味の正典は AOT バイナリ — インタプリタと AOT が食い違ったら AOT バイナリが勝つ。
カウンタクラス
Lambda Prelude は関数型で、値は不変、クラスはインスタンス変数を持たない。各オブジェクトは fields: 上の不変レコード。
Object subclass: #Counter fields: (value).
Counter class >> of: n = Counter fields: { value: n }.
Counter >> value = value.
Counter >> inc = Counter fields: { value: value + 1 }.
c := Counter of: 41.
c inc value printNl. "=> 42"
inc は新しい Counter を返す。ユーザコードに := 再代入はない — 束縛は let (トップレベルでは :=) のみ。
分岐は多態ディスパッチ
表層に if / match は存在しない。分岐はすべてメッセージ送信で書く。
(score > 90)
ifTrue: ['A' printNl]
ifFalse: ['B' printNl].
name ifPresent: [:n | n printNl]
ifAbsent: ['anonymous' printNl].
xs ifEmpty: ['empty list' printNl]
ifNotEmpty: [:h :t | h printNl].
これは伝統的な Smalltalk と同じ流儀を、条件分岐 / Option 分解 / リスト分解 / retry / timeout すべてに一貫して適用したもの。
プロトコルが継承の代替
クラスは継承しない。共通振る舞いは型クラスに相当する Protocol で表現し、クラスは protocols: 節で実装するプロトコルを宣言する。
Protocol subclass: #Comparable
requires: (#<).
Comparable >> > other = other < self.
Comparable >> min: o = (self < o) ifTrue: [self] ifFalse: [o].
Comparable >> max: o = (self < o) ifTrue: [o] ifFalse: [self].
Object subclass: #Score
fields: (points)
protocols: (Comparable).
Score class >> of: n = Score fields: { points: n }.
Score >> points = points.
Score >> < other = points < other points.
((Score of: 10) max: (Score of: 3)) printNl.
Score は < だけを実装すれば > / min: / max: が Comparable 経由で自動的に手に入る。
2 つの実行モード
同じソースを 2 通りに実行できる。標準ライブラリ・型システム・アクターモデルは共有される。
| 機能 | インタプリタ | AOT |
|---|---|---|
| ツリーウォークによる反復開発 | あり | — |
| 静的に解決できる送信を OCaml 直接呼び出しに | — | あり |
| 動的送信用ランタイムディスパッチャ | あり | あり |
| マルチドメインワークスティーリングスケジューラ | あり | あり |
Dynlink プラグイン (PostgreSQL / MariaDB / MySQL / Redis) | あり | あり |
反復開発は run、本番は build — AOT バイナリは静的に解決できる送信を直接 OCaml 呼び出しに落とし、それ以外は実行時のメソッドディスパッチを通す。
CLI リファレンス
lambda-prelude run script.lp # ツリーウォークインタプリタ
lambda-prelude build script.lp --out binary # AOT ネイティブ
lambda-prelude test tests # Test API のテスト実行
lambda-prelude new my-service # プロジェクト雛形
デバッグツールは次のとおり。
lambda-prelude run script.lp --trace-mailbox # メールボックス投入 / 受信を逐次ログ
lambda-prelude run script.lp --quiet # 未マッチメッセージ警告とアクター異常死の報告を抑止
lambda-prelude run script.lp --scheduler multi 4 # N ワーカードメインプール + 自動分散
lambda-prelude build script.lp --emit # 生成された OCaml を標準出力に
lambda-prelude build script.lp --out script.ml # 生成された OCaml をファイルにモジュールの解決
module Main imports: (Http, MyLib). の名前は、そのファイル自身のディレクトリ → LAMBDA_PRELUDE_PATH に並べたディレクトリ → stdlib の順に探す。区切りは PATH と同じ (Windows は ;、POSIX は :)。同名の .lp が無い単一セグメントの名前 (Torm など) はプラグインに解決される。
LAMBDA_PRELUDE_PATH=src lambda-prelude run tests/my_test.lp # tests/ から src/ のモジュールを読む
run / build / 言語サーバ / デバッグアダプタが同じローダを共有しているので、run で解決する名前は build でも編集中でも解決する。解決に失敗した時のエラーは、実際に辿ったディレクトリを列挙する。
エディタ
VS Code 拡張 (lambda-prelude) がシンタックスハイライト、言語サーバ (構文エラーと型エラーの診断、推論型の hover、補完、定義へ移動、参照検索、改名)、デバッグアダプタ (ブレークポイント、ステップ実行、変数の表示 — インタプリタ上で動く) を提供する。hover はカーソルを含む最も内側の式に答える。
拡張は言語サーバ・デバッグアダプタ・stdlib・プラグインを同梱しているので、ワークスペースにビルド成果物が無くても imports: と dao: を解決して診断できる。設定は lambdaPrelude.importPath (上の LAMBDA_PRELUDE_PATH に相当。ワークスペースルートからの相対で書け、言語サーバ・デバッグアダプタ・Run コマンドの全部に渡る) と、同梱バイナリの代わりを指す serverPath / cliPath / debugAdapterPath。