Next
No newer accessible resource.
2026-09-19 14:01
lkjscriptを試すために作らせたゲーム
Next
No newer accessible resource.
ソフトウェアの作成や保守に、AIエージェントが関わる場面が増えてきました。しかし、現在広く使われている言語の多くは、人間が読み書きすることを前提に作られています。そのため、エージェントが書いたコードが型や所有権の規則を満たしているか、ある関数がどの権限を使うのか、依存パッケージを更新したときに結果が黙って変わらないか、といった点を、言語の仕組みとして機械的に確かめにくい場面があります。
lkjscriptは、こうした問題を言語の設計から見直すために開発しているプログラミング言語です。本記事では、現時点での設計の考え方と開発状況を紹介します。内容は2026年10月8日時点のリポジトリ(開発版 0.1.88)に基づいています。言語に関心のある方や、同じ問題意識を持つ方に向けた記事です。
lkjscriptは、型付きのプログラムを一つの実行ファイル lkjscript から作成、検査、変更、テスト、実行するための言語であり、エージェントが利用する基盤でもあることを目指しています。ライセンスは Apache-2.0 です。
最も特徴的なのは、プログラムの編集権限がテキストファイルにないことです。権威あるプログラムは「受理済みの型付き意味グラフ」であり、テキストはその変更を提案するものとして扱われます。宣言には安定した識別子が付き、名前は変更可能な参照子にすぎないため、名前を変えても宣言の連続性は保たれます。
実際に動かすには、次のコマンドを実行します。
lkjscript new hello --template command --name hello
lkjscript --project hello check
lkjscript --project hello run main
実行結果は "hello" です。lkjscript capabilities を実行すると、その実行ファイルが対応する操作、文法、権限の境界、上限を機械可読な形で取得できます。エージェントが言語の現状を問い合わせるための入口として設計されています。
次は、引数を二乗する関数 square の定義の抜粋です。実際の提案には request 行や declarations の囲みも必要です。
(function create square (visibility public)
(parameter create value (type I64))
(returns I64) (effect pure)
(body (call std::multiply (local value) (local value))))
提案は change plan で検証され、得られた計画トークンを指定して change apply を実行したときにだけ反映されます。基準となる版が古くなっていたり、検証に失敗したりした場合は、一部だけが反映されることはありません。別の提案が先に反映されたときも、元の提案と計画を保ったまま基準版を更新して再審査できます。意味が衝突する場合は拒否され、互いに独立した変更は同一性を保って合成されます。
値の受け渡しは、borrow(借用)、consume(所有権の移動)、unrestricted(制限なし)のいずれかのモードで表されます。次は、所有者を受け取り、同じ型の値として返す関数の一部です。
(parameter create owner (type T) (use consume))
(returns T) (effect (task (requirement R) (parameter E)))
consume で渡された値は、呼び出し側では使えなくなります。所有権は関数の中で受け渡され、返された値が新しい所有者になります。読み取りだけの場合は borrow を使い、その借用が結果のどの引数に由来するかも型として追跡されます。
関数は pure(純粋)か task(タスク)のいずれかです。純粋関数は、時刻、乱数、ネットワーク、ストレージといった能力を使えません。タスクは、使う能力の要件を効果の行として宣言します。先ほどの例で R は、時刻の能力 std::WallClock を表す要件です。
重要なのは、型で要件を宣言することと、実行を許可することが別々の仕組みである点です。実行時の権限は、デプロイの記述子で能力ごとに明示的に与えます。与えられていない要件があれば、実行前に拒否されます。型の上で制約を満たしていても、それだけで実行権限が生まれるわけではありません。
契約(トレイトに近い概念で、owned contract と呼ばれます)を満たす実装が複数あるとき、どの実装を使うかは呼び出しの中で明示されます。あとから依存パッケージを追加したときに、解決先が暗黙に入れ替わらないようにするためです。
コンパイルされた成果物 .lkja は、バイトコードのVMで実行されます。さらに、純粋なコマンドとグラフテストは、独立に実装された参照インタプリタでも実行され、両者の結果が食い違えば失敗になります。
実行は、作成用のプロジェクトから切り離せます。lkjscript run --deployment は、実行ファイル、成果物、記述子があれば動作します。HTTPサーバー、WebSocketによる対話セッション、耐久キュー、データストア、TLSを用いた外部へのHTTP GETといった機能は、型付きの仕組みとして言語から利用できます。
実際のアプリケーションとして、名前付きのテキストを保存し、受理された更新ごとに不変のスナップショットを残す lkjournal も、同じ言語で書かれています。2,000を超える意味上の要素で構成され、ドメイン、サービス、ワーカーの各モジュールに分かれています。
開発版は 0.1.88 で、確認できた最新の公開リリースは v0.1.83 です。この番号は世代や互換性を示すものではなく、識別子として扱われています。公開バイナリは Linux x86-64 向けの静的リンク(musl)です。
処理系は Rust 1.98.0 を固定して開発しており、Rustのソースは約47万行です(テストや検証ツールを含みます)。本体クレートでは unsafe の使用を禁止し、clippy では unwrap や panic も禁止しています。記録されている受理済みのソース版では、ワークスペースのテスト2,231件が成功し、全体検証の26ゲートも通過しています。
ただし、安定性、互換性、サポートについての約束はありません。破壊的変更も行われ得ます。また、受信側のTLS終端、保存データの暗号化、悪意あるコードのサンドボックス、JITやAOTによるコンパイル、マルチテナントの分離、成果物への署名、レプリケーション、分散合意は、まだ実装されていません。
開発は、エージェントに工学的な判断を委任する体制で進めています。方向性と最終的な受け入れは、プロジェクトの所有者が判断します。作業の経緯は docs/campaigns、設計判断は docs/decisions に残しています。
ロードマップ上の主な項目は、所有権付きの値と汎用的な契約を組み合わせる仕組み、メモリ領域(リージョン)の管理方針、構造化された並行処理、同一プロセス内で複数のアプリケーションを共有するランタイムです。メモリ管理については、全体を追跡するグローバルなGCを必須にせず、所有権、局所的なトレース、不変の共有領域を組み合わせる方針を検討しています。
同時編集への対応も、将来必要な機能として位置づけています。ただし、現在のCLIがそれを完全に扱えるわけではなく、段階的に進めている段階です。
長期的には、言語の処理系を言語自身で書く自己ホスティングを目指しています。現在の処理系はRustで書かれており、Rustの置き換えは目標であって、まだ実現していません。
lkjscriptは実験段階にあり、業務システムに採用できる状態ではありません。それでも、「エージェントが検査できる契約を言語の中心に置く」という方向性には、検討する価値があると考えています。関心のある方は、GitHubリポジトリ(https://github.com/lkjsxc/lkjscript)のREADMEと docs/direction.md をご覧ください。