ソースからビルド

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。

次のステップ