世界を動かす技術を、日本語で。

左から右へのプログラミング

2025年8月19日原文(graic.net)

概要

  • プログラムは 入力途中でも有効 であるべきという原則の重要性
  • Pythonのリスト内包表記やC言語の関数呼び出しの エディタ補助の難しさ
  • RustやJavaScriptの 左から右への構築 による補完性の高さ
  • API設計とプログレッシブディスクロージャー の観点からの比較
  • 良いAPI設計 がユーザー体験を大きく向上させる事例の紹介

プログラムは入力途中でも有効であるべき

  • Pythonのリスト内包表記 は、書き始めの段階でエディタの補完が機能しない問題
    • 例:[line.split() for line in text.splitlines()]line は宣言前で補完不可
    • split()メソッドが存在するかも書き終わるまで不明
  • エディタの補完機能 は変数や型が明確になった時点でしか有効にならない現状
  • Rustの例 では、左から右へとプログラムを構築することで、逐次的に補完が可能
    • 例:text.lines().map(|line| line.split_whitespace())
    • lineを宣言した瞬間からメソッド補完が可能
  • Haskell の例(map words $ lines text)は原則から外れるが、最もエレガント

プログレッシブディスクロージャーとAPI設計

  • プログレッシブディスクロージャー :必要な複雑さだけをタイミングよく提示する設計原則
    • 例:Wordで画像を挿入した時にだけ画像回り込みオプションが出現
  • C言語の関数呼び出し は、構造体にメソッドがないため、関数名を知っていなければならない不便さ
    • 例:FILE *fileの場合、file.readのように補完できず、freadなど関数名を探す必要
    • 関連するcloseメソッドの存在を直感的に知ることができない
  • Pythonや他言語 でも同様の発見性の低さが課題
    • 例:len, length, size, countなど長さ取得関数の命名が統一されていない

JavaScriptとRustの良い例

  • JavaScript では、text.split(" ").map(word => word.length)のように、左から右へと構築しながら補完が機能
    • word.lと入力した時点でlengthが補完候補に出現
    • map関数も型に合ったものだけが補完
  • Rust も同様に、チェーン式で型安全かつ補完が効く
  • Python は関数型スタイル(map(len, text.split()))で可読性は高いが、発見性や補完性で劣る

複雑なロジックにおける可読性

  • Pythonの複雑な内包表記 は、ネストや条件式が増えると可読性が著しく低下
    • 例:len(list(filter(lambda line: ... , diffs))) のように括弧や条件の対応が分かりづらい
  • JavaScript では、diffs.filter(...).length のように左から右へ意味が明確に伝わる
    • ロジックの流れが直線的で理解しやすい

結論:良いAPI設計の重要性

  • プログラムは常に有効な状態であるべき という設計思想
    • 入力途中でも型やメソッドが明確になり、エディタ補完やREPLでの即時確認が可能
  • API設計 はユーザー体験を大きく左右
    • 左から右へと構築できるAPIは、発見性・補完性・可読性が高い
  • より良いAPIとエディタ体験 を目指すことの重要性

Hackerたちの意見

プログラムは、入力された通りに有効であるべきだよね。もし開発者がいつも左から右へ、1文字ずつ、1行ずつコードを書くなら、すごくいいと思う。でも現実は、いろいろなところに飛び跳ねながら、いくつかのことを埋めたり、他のことを unfinished にしたりしながら戻ってくることが多いんだ。時々、変数で動作するコードを書いた後、1分後にその変数を宣言することもあるし、テスト用の値を割り当てたりもする。

その通り。新しいファイルの時だけ、コードを順番に書くよね。もしクラスに新しいフィールドを追加しようと思ったら、必ずしもクラス定義から始めるわけじゃない。アイデアが浮かんだ時に IDE がそのフィールドを使ってたから、まずはそのコードを書くことが多い。条件チェックを強化したい時も、if や else を並べ替えている間は、そのコードが有効じゃない時期があるよね。

うん、強制するのはちょっと無理がある制限に思える。

この記事のテーマとはちょっと違うけど、面白いよね。

これは「彼を料理させてあげよう」ってやつだね。記事を読んでいくと、著者がプログレッシブ・ディスクロージャーの哲学について主張してる。最後の段落でまとめていて、合理的な意見だと思う。> 「テキストを入力した時点で、プログラムは有効です。text.split(" ")を入力した時点で、プログラムは有効です。text.split(" ").map(word => word.length)を入力した時点で、プログラムは有効です。プログラムを構築する過程で有効であるため、エディタがサポートしてくれるんです。REPLがあれば、プログラムを入力しながら結果を見ることもできる。CoPilotやエージェントコーダーの時代に、エルゴノミクスがどれほど重要かは分からないけど、LSPをコーディングすることは確かにその主張に満足感を与えるだろうね。

コードは一度書かれて、何度も読まれるからね。順番に読めるコードは、ジャンプしなきゃいけないコードよりも速くて読みやすい。

これには同意するけど、もう一つの原則に繋がるんだよね。多くの言語がこれを無視してるけど、書き終わってないからってコンパイルに失敗するのはおかしいよ!他のブロッキングしない方法で失敗すべきだと思う。でも、いくつかの言語は、戻り値がなかったり未使用の変数があるとエラーを出しちゃうから、それができないんだよね。

あと、相互再帰もね。 ;)

IDEが俺が実際に入力する順番とは違う順番で入力すると思ってるのは、確かにちょっとイライラするね。

SQL はこの問題があって、SELECT リストを FROM/JOIN の前に要求するんだよね。入れ替えられる SQL 系のものも見たことがあるけど、全部入れ替え可能にすべきだと思う。

なんで Python がこんなに人気なのか分からない。1人以上が関わると、痛い言語だよ。著者が言ってることは氷山の一角に過ぎない。

初心者にはいい言語だけど、残念ながら多くの人が他の言語を学ぶのは難しくて無駄だと思ってる。

Hacker Newsで議論の続きを見る