AIが第一の書き手になったら、プログラミング言語の設計原理は反転する
2026年6月9日、Claude Fable 5 がリリースされた。
自分の能力の限界を、明確に超えた道具だった。Anthropicは当初、6月22日まではサブスクリプションでこのモデルを使えると告知していた。たっぷり時間がある——そう思って、普段なら絶対に手を出さない試みをぶつけてみることにした。
たとえば、プログラミング言語を一から作る、というような。
結局、その時間は与えられなかった。Fable 5 はリリースのわずか3日後、「強力すぎる」という理由で停止された。22日どころか、12日に消えた。残されたのは、その3日間で生まれた Kei という言語だった。
だが、消えても困らなかった。設計——思想の骨格、何を保証し何を捨てるかの判断、トレードオフの解きほぐし——は、Fable 5 が手の中にあるうちに固めきっていたからだ。一番発散しやすく、一番難しい部分を、限界を超えた道具で一気に収束させていた。道具が消えた後に残ったのは、収束済みの設計図だった。あとはそれをOpusに渡して、実装に落とし、派生させるだけでいい。難しい判断を強い道具で、その展開を手堅い道具で。
奇しくもそれは、Keiが言語として体現しようとした分業——難しい判断は誰かが決め、実装は別の書き手が継ぐ——そのものだった。後で気づくのだが、筆者はこの言語を作りながら、その言語の思想を、自分の作業フローの中で先に実践していたことになる。
この記事は、その言語そのものの宣伝ではない。作る過程で筆者が掴んだ、ひとつの問い——「AIが第一の書き手になったとき、プログラミング言語の設計原理はどう変わるのか」——についての記録だ。
既存の言語は、全部「人間はタイピングが嫌い」を前提にしている
少し立ち止まって考えてほしい。我々が日々使っている言語の機能の多くは、ある一つの前提から逆算されている。人間は書くのが面倒くさい、という前提だ。
型推論は「いちいち型を書きたくない」から生まれた。省略記法も、暗黙の型変換も、演算子オーバーロードも、根っこは同じ。「人間の手数を減らす」ことに最適化されている。これは正しい。書き手が人間である限り、手数は最大のコストだ。
ところが、書き手がAIになると、この前提が崩れる。
AIにとって、書くコスト(トークンを出力すること)はほぼゼロだ。冗長な記述を厭わない。むしろAIが苦しむのは別のところにある。「このコードが正しいと確認するコスト」と、「文脈の外側にある何かに依存していて、その場では意味が読み取れないコード」だ。
つまり、書き手がAIに変わると、コストの構造が丸ごと入れ替わる。手数は安くなり、検証と局所性が高くつくようになる。 ならば、言語の設計原理もそこに合わせて反転させるべきではないか——これがKeiの出発点だった。
反転させると、こうなる
人間向けの最適化を捨て、検証可能性・推論の局所性・エージェントループとの親和性に全振りすると、設計判断はこう変わる。
- 冗長でも、曖昧さをゼロにする。 トークンは安い。曖昧さは高い。暗黙の型変換も、暗黙のimportも、演算子オーバーロードも置かない。1文字でも短く、ではなく、1ミリでも疑いなく。
- エラーは構造化データを正とする。 人間向けの散文メッセージは派生物にすぎない。AIにとって価値があるのは、位置・原因・修正候補が機械可読なJSONで返ること。生成→検証→修正のループの帯域が、そのまま言語の価値になる。
- 推論の局所性を仕様で保証する。 1ファイルの挙動は、そのファイルと明示的なimportだけで決まる。再エクスポート禁止、グローバル可変状態なし。AIが「この関数、どこか遠くの状態に依存してないか?」を疑わなくていい。
- 正規形を一つに固定する。 同じ意味のコードは、常に同じ字面に整形される。スタイル論争を仕様で殺す。差分が常に意味的になる。
人間がこれを手で書いたら、たぶん発狂する。冗長で、宣言だらけで、近道が一切ない。でも、書くのはAIだ。だから問題ない。
人間は、何を読むのか
ここで当然の疑問が出る。AIに全部書かせるなら、人間の仕事は何が残るのか。
Keiの答えは、 「人間は実装(body)を読まない。契約を読む」 だ。
func withdraw(account: AccountId, amount: Money) -> Result<Money, WithdrawError>
uses Database.Read, Database.Write
requires amount > Money.zero
ensures result.isOk implies amount > Money.zero
{
let current = Database.fetchBalance(account) else fail WithdrawError.NotFound(account)
if current < amount {
return Err(WithdrawError.Overdraft { limit: current })
}
Database.setBalance(account, current - amount)
return Ok(current - amount)
}
注目してほしいのは、関数のシグネチャに3種類の「条項」が乗っていることだ。
usesは権限条項。この関数はDBの読み書きをする、それ以外の外界には触れない、とAIが宣言する。宣言にないエフェクト(たとえばネットワークアクセス)を本体で使えば、コンパイルエラーになる。requiresは呼び出し側の義務。amountは正であること。ensuresは実装側の誓い。この関数が世界に対して保証すること。
人間がレビューするのは、この3行で十分だ。「DBしか触らない、入力は正、結果はこうなる」——これに署名(承認)できるなら、bodyの中身はAIに任せていい。レビューの単位が、実装から契約へと圧縮される。
筆者はこの言語のタグラインをこう決めた。
Code is a 契.
「契」は、言語名 Kei(契の音読み)の由来でもある。コードとは、人間とAIが交わす合意書だ。読めない合意書にサインさせるのは詐欺だが、契約条項さえ明示されていれば、人は安心して署名できる。
新しい言語の死因と、その回避
ここまでは思想の話だ。だが、思想だけなら誰でも語れる。新言語には、思想とは別の、もっと身も蓋もない死因がある。
学習データが存在しないことだ。
新しい言語は、AIの学習データに含まれていない。だからAIは書けない。書けないから誰も使わない。使われないからデータが増えない——この悪循環で、ほとんどの新言語は生まれた瞬間に詰んでいる。AIが書き手の中心になった時代では、この死因はむしろ深刻になった。
Keiの賭けは、ここにあった。「学習データがなくても、取扱説明書(Skill)を作り込めば、AIは推論で書けるのではないか」。文法を暗記している必要はない。正しい文法を引けて、間違いを即座に指摘される環境があれば、書けるはずだ、と。
これを検証するために、ひとつ実験をした。Keiを一切学習していないエージェントに、Keiの取説(Skill)だけを渡し、「図書館の貸出システムを書け」と指示した。文法は一切教えない。「外部ストレージに書き込む」「失敗の理由が型で伝わるように」といった、自然言語の要件だけを渡した。
結果、エージェントは一度の指示で、検証を通るコードを書き上げた。一部を抜粋する。
func borrowBook(memberId: MemberId, bookId: BookId) -> Result<Loan, BorrowError>
uses Clock, Database.Read, Database.Write
{
let member = findMember(memberId) else fail BorrowError.MemberNotFound(memberId)
let book = findBook(bookId) else fail BorrowError.BookNotFound(bookId)
if book.available <= 0 {
return Err(BorrowError.NoStock(bookId))
}
let current = countActiveLoans(memberId)
if current >= member.loanLimit {
return Err(BorrowError.LimitReached { limit: member.loanLimit, current: current })
}
// ...貸出記録を作成して保存...
}
「外部ストレージに書き込む」という日本語の要件を、エージェントは自力で uses Clock, Database.Read, Database.Write という権限条項に翻訳していた。「見つからないかもしれない取得」を Option で、「明確な失敗理由」を Result で、と型を使い分けてもいた。誰も文法を教えていないのに、だ。
さらに筆者が舌を巻いたのは、エージェントが契約を純粋関数に切り出していたことだ。「在庫がちょうど1減る」という数量的な約束を、副作用のあるborrowBook本体に直書きせず、純粋なヘルパー関数の ensures に逃がしていた。
func decrementAvailable(available: Int) -> Int
requires available > 0
ensures result == old(available) - 1
{
return available - 1
}
これはKeiの仕様(契約式は副作用禁止)を正しく理解していなければ書けない設計判断だ。取説を読んだだけのエージェントが、そこまで汲んでいた。
学習データの不在は、取説の作り込みで埋められる。少なくともこの一例では、賭けは成立した。
同じ山を、別のルートで登っていた人がいた
この設計を進めるうちに、筆者は自分とほぼ同じ思想の言語が、すでに世に存在することを知った。形式検証を備えた「エージェントファースト言語」で、契約をコードに持たせ、コンパイラがエージェント向けの構造化出力を吐く——コンセプトの芯が、不気味なほど一致していた。
最初は落ち込んだ。だが、見ているうちに考えが変わった。
その言語が狙っているのは、システムプログラミングの領域だ。C的なプリミティブ型を持ち、数学的な証明を本格的に回し、ジェネリクスやクロージャを検証範囲を狭く保つために意図的に削ぎ落としている。「正しさを証明する」ことに振り切った、研究寄りのシステム言語だ。
一方、筆者のKeiが狙っているのは真逆だった。TypeScriptにトランスパイルしてWebやエッジで動く、業務ドメインを型で縛る実務言語。証明は将来の宿題として、まずは実行時アサーションで実用を取る。
同じ「AI時代の検証可能な言語」という山を、片方は形式手法の北壁から、もう片方はWeb実務の南壁から登っていた。山は同じ、ルートは別。独立した二人が同じ山を見つけていたという事実は、むしろこの問題設定が本物であることの傍証だった。素人の思いつきではなかった、ということだ。
(ちなみにこの過程で、言語名は二度死んでいる。最初の候補は既存のブロックチェーン言語と衝突し、次の候補は前述の先行言語と完全に同名だった。名前は惚れる前に名前空間を調べろ、という教訓を、筆者は二度払って学んだ。)
まだ解けていないこと
誠実に書いておく。この実験は、Keiが完成したことを意味しない。むしろ、穴を一つあぶり出した。
先ほどの図書館システムで、エージェントが書いた borrowBook には、関数全体としての ensures がなかった。「この関数を呼んだ結果、その書籍の在庫がちょうど1減る」という事後条件は、純粋ヘルパーの契約としては表現されていたが、borrowBook自身の契約としては書かれていなかった。
これはエージェントの怠慢ではない。Keiの仕様に、まだ書く手段がないのだ。エフェクトを持つ関数の事後条件で、外部ストレージの状態変化(old(在庫) と新しい在庫の関係)をどう参照し、どう契約するか——そこが設計されていない。
検証を通った(kei check がエラーゼロを返した)ことと、正しいことは、別だ。書かれていない契約は、検査されない。ドッグフード実験の本当の収穫は「書けた」という成功ではなく、「エフェクトと契約の境界に、まだ言語化されていない領域がある」という発見の方だった。これは次のバージョンへの宿題になる。
問い返し
筆者が3日間で得たのは、完成した言語ではなく、一つの視点だった。
書き手が人間からAIへ移るとき、言語は「書きやすさ」を最適化する道具から、「検証しやすさ」と「合意のしやすさ」を最適化する道具へと、設計の重心を移す。コードは、人間が機械に命令するための文ではなく、人間とAIが互いの責任範囲を取り決める合意書になる。
あなたが普段使っている言語の機能のうち、どれが「人間は書くのが面倒」という前提から来ているだろうか。その前提を外したとき、何が残り、何が要らなくなるだろうか。
AIが第一の書き手になる時代に、あなたの言語観はどうアップデートされるだろうか。筆者はまだ、その問いの入り口に立ったばかりだ。
Keiはまだv0.1の実装フェーズにある実験的な言語です。この記事は「AI時代の言語設計とはどうあるべきか」という問いを共有するためのもので、Keiの利用を勧めるものではありません。同じ問いに興味を持った方と、設計の議論ができれば嬉しいです。