Austin Z. Henley が、Cで「Pythonのごく一部」を 1024バイトに収める実験を公開した。狙っているのは、CPythonのような本格実装ではなく、見た目がPythonらしく振る舞う最小限のインタプリタだ。しかも今回は、コードゴルフの技巧をただ見せたいのではなく、どう削ればここまで小さくできるのかを、途中で何度も失敗しながら追っているのが面白い。完成物は短い。だが、その短さに行き着くまでの割り切り方はかなり過激だ。
Henley は週末に手でコードを書くと「人間に戻れる」と書いたうえで、512バイトか1024バイトのCコードでPythonインタプリタを作る挑戦を始めた。もちろん、本物のPython全部は入らない。そこで彼が考えたのは、「Pythonっぽく見える最小構成は何か」を選び抜くことだった。例として最初に挙げたのは FizzBuzz で、def、コロン、インデント、if に括弧がない書き方など、見た目がPythonそのものに見える断片を確認していく。
最初の試みは512バイトを目指したが、すぐに無理だと分かった。式を足し算できるようにし、変数代入を入れ、if x > y: z = 3 のような文まで扱えるようにしてみたものの、できあがったのは「電卓みたいなもの」で、本人が思っていたPythonには届かなかった。そこで方針を変え、「まず動かしてから、削れるだけ削る」へ切り替える。
実装の中身もかなり割り切っている。CPythonのように字句解析、構文解析、AST、バイトコード生成と段階を踏むのではなく、ソースをその場で読みながら実行する。状態はグローバル変数に押し込め、ソース全体はスペースの大半を取り除いた固定長配列に置く。変数名も関数名も単一文字にし、シンボルテーブルも256要素の配列で済ませる。エラー処理はほぼなく、キーワードが正しく書かれている前提で進む。変数名は小文字1文字だけ、文字列の中以外では空白を捨てる、といった制約も大胆だ。
制御構造の扱いも工夫がある。インデントの深さを見てブロックを抜けるときは、Cの再帰呼び出しをそのまま使う。ループはコンパイルせず、ソース内の条件式の位置に戻って読み直すことで実現する。while も for x in range(y) も、実行後に開始地点へジャンプして再評価する仕組みだ。関数定義と呼び出しも同じ発想で、定義時にソース中の位置を覚え、呼び出し時にそこへ飛んで、終わったら元の位置へ戻す。
さらに byte を削るため、Henley は Stack Overflow の古い code golfing の知恵を大量に借りたという。ASCII値を直接使う、三項演算子やカンマ演算子を使う、論理演算の代わりに bitwise 演算を使う、C89のゆるい仕様に寄りかかる、といった手段だ。たとえば式の足し算処理は短く圧縮され、行末まで飛ばす関数も if ではなく && を使う形まで縮んだ。最終的に、読みやすい版は 4800バイト超、ゴルフ後の版は 1024バイトに収まった。実装できた機能は、単一文字の整数変数と整数リテラル、代入、+ - * % の優先順位つき演算、比較、整数の真偽値、if と else、while と for の else、引数なし関数、再帰呼び出し、インデントベースのブロック、文字列1個か整数式を print する機能、コメントまでだ。本人は、もう当分 code golf はやりたくないとも書いている。
この話でいちばん面白いのは、1024バイトという数字そのものより、どこを捨てれば Python らしく見えるかをかなり真面目に考えている点だと思う。Pythonらしさは、実は構文の全部ではない。インデントでブロックが分かれること、def で関数が書けること、if と while が読めること、そして print と四則演算があること。このへんがそろうだけで、人はかなりPythonだと感じる。逆に言えば、例外処理、スコープ、豊富な型、構文の厳密さがなくても、「Pythonっぽさ」は残せる。Henley の実験は、その感覚をかなり露骨に可視化している。
しかも、彼は最初から最適化の話をしていない。「まず動かす、あとで小さくする」と言っているのが重要だ。小規模な実装でも、先に設計を詰めすぎると手が止まる。逆に、いったん動くものを作ると、削る場所が見えてくる。これは code golf に限らず、CLIツールや実験的なパーサーを書くときにも効く考え方だと思う。完成品の綺麗さより、不要物を見抜くための雑な試作が先にある。そこがこの投稿の芯にある。
この記事が単なるネタで終わっていないのは、実装が「雑」だからではなく、むしろ実行の本質を別の形でなぞっているからだと思う。CPythonは、字句解析して、構文木を作って、最適化して、bytecode にして、それを走らせる。一方で Henley の版は、その場で読んで、その場で計算し、その場で飛ぶ。中間表現を極限まで減らし、ソースそのものを状態として扱っている。これは本格的な言語処理系の教科書とは真逆だが、「入力をどう読むか」「どこに状態を持つか」という点では、むしろ実装の素顔が見える。
ただ、このやり方はかなり危うい。エラー処理を捨て、変数名を1文字に絞り、空白も大半を消してしまうと、書き手の自由度はほとんどなくなる。教育用や実験用なら楽しいが、実用品には向かない。だから私は、この作品を「小さなPython」と呼ぶより、「Pythonの見た目を借りた実行機械」と見たほうが正確だと思う。見た目はPythonでも、保守性や安全性はほとんど別物だ。そこを割り切っているから、逆に技術としてはきれいに立っている。
一方で、こういう極端な圧縮は、普段の開発にも少しだけ返ってくるものがある。たとえば、グローバル変数を増やしすぎると何が壊れるか、再帰でブロックを表すとどこが見通し悪くなるか、文字列をそのまま走査する実装はどこで限界を迎えるか。Henley はそれを、わざわざ壊れやすい形で露出させている。だから読んでいて、コードゴルフのうまさ以上に、言語実装の制約の置き方が見えてくる。
ただし、これは模範解答ではない。むしろ、普通の開発では真似しないほうがいい手が多い。C89 の曖昧さに頼る、ASCII値を直接使う、キーワードの綴りを前提にする、そうした手法は短さを買う代わりに、読みやすさと安全性を払っている。なので、この投稿の価値は「このコードを使えるか」ではなく、「どこまで削るとプログラミング言語の核だけが残るか」を見せた点にある。技術記事としても、遊びとしても、かなり贅沢な実験だと思う。