1 ポイント 投稿者 GN⁺ 2025-09-09 | 4件のコメント | WhatsAppで共有
  • 過去の Ada開発環境 では、コードフォーマットの問題はすでに解決されていた
  • 開発者は DIANA中間表現(IR) の形でコードを扱い、それぞれが望むプリティプリント設定で閲覧していた
  • 現代でも linter やフォーマット方針などをめぐる 非効率と議論 が繰り返されている
  • 当時の Rational R1000ワークステーション は、革新的な開発環境と機能を提供していた
  • コーディングのフォーマット問題について、一世代前の方式を参考に 今日の開発慣行を変える ためのアイデアを提案している

コードフォーマット論争 ― 1980年代の解決策

  • 筆者は、高校時代にAdaコンパイラの作業に参加していたコンピュータサイエンス教師、Mr. Paigeとの経験に触れている
  • 2016年に linter ツールの設定の煩わしさに不満を述べ、「なぜ今でもこんな問題に悩まされなければならないのか」と尋ねた際、すでに40年以上前にこの問題が解決されていたという話を聞いた

AdaとDIANAの登場

  • Ada開発者は テキストのソースコード を保存する代わりに、DIANA(Descriptive Intermediate Attributed Notation for Ada) という中間表現を利用していた
  • 各開発者は自分専用の プリティプリント設定 でソースを好みの形で閲覧できた
  • フォーマット論争や linter の問題は存在せず、エディタではプログラムツリーを直接編集できた(現代の projectional editing に似ている)

Rational R1000 ― 先駆的な開発環境

  • Rational R1000 ワークステーションは、インクリメンタルコンパイル静的解析バージョン管理デバッグ など、さまざまな高度機能を内蔵していた
  • DoD などの政府プロジェクト、国際宇宙ステーション(ISS)、F-22戦闘機など重要なソフトウェア開発に活用され、UMLの誕生にも貢献した
  • Grady Boochによれば、R1000はDIANAベースのマシンであり、ソースコードを保存せず、DIANAツリーのプリティプリントのみを使用していた

DIANAベース開発の利点

  • フォーマット論争や linter の設定、エディタ環境の統一が不要だった
  • ハードウェアアクセラレーションにより、インクリメンタルコンパイル、容易なリファクタリング、迅速な統合など、革新的な開発体験を提供した
  • 開発効率や大規模システム開発に重要な影響を与えた

今日への示唆

  • ハードウェアアクセラレーションによるコンパイルの重要性は下がったが、フォーマット問題の解決はいまだ不十分である
  • 主流の方式が projectional editing やライブ環境ではないとしても、過去のアプローチのように より効率的で議論の少ない開発慣行の導入 を考えるべき時期に来ている

参考資料

  • 筆者はこのテーマを調査する中で、R1000に関するさまざまな文書や技術報告書を引用している

4件のコメント

 
ndrgrd 2025-09-10

共有コードはすでに統一された設定で自動フォーマットする機能がありますし、企業でも広く使われているものだと認識しています。

 
euphcat 2025-09-10

自動フォーマットが論点なのではなく、特定のフォーマットが優れているという認識や、自分のフォーマットを捨てて見慣れないフォーマットに適応しなければならない過程そのものが不要だ、という話のようです。フォーマットに縛られない中間表現を保存しておき、ユーザーごとに自分が使いやすい形でプリティプリントすればよい、という理屈です。

 
ndrgrd 2025-09-10

自動フォーマットによって、中間表現がなくても既存の言語でまったく同じことができる、という話をしたかったのですが、説明が足りませんでしたね。

 
GN⁺ 2025-09-09
Hacker Newsの意見
  • なぜ人々が linter の設定をそこまで気にするのか理解できない。これは明らかに無意味な論争で、ただ1つに決めて linter を自動で回して終わりにすればいい。実際のソフトウェアエンジニアリングに使う時間のほうが重要だと思う。どんなフォーマットでも、チームで決めて使っていれば1週間以内に慣れる。
    • ソースコードフォーマッタと lint プログラムは別物だという点を指摘している。フォーマッタはコードのレイアウトだけを整え、linter はコード内の潜在的なバグやエラーを見つけてくれる。両方をサポートするツールもあるが、それは実装上の詳細にすぎない。lint が何かについては https://en.wikipedia.org/wiki/Lint_(software) を参照できる。
    • 私はほとんどの時間をコードを「読む」ことに使っていて、コードのレイアウトは読む速度に関係している。違うレイアウトに慣れることはできるだろうが、すべてのレイアウトが同じだけ読みやすいわけではない。私はコードを視覚的なパターンで記憶し、レイアウトに基づいてコードを断片化して読む。逆説的だが、心の目で視覚化できない aphantasia を持っているにもかかわらず、視覚的な手がかりでよりよく記憶する。
    • ある種の linter 設定には明確に利点がある。たとえばテーブルに trailing comma を使えば、最後の行に新しい項目を追加するとき1行だけ編集すればよく、diff も簡潔になる。だから悪い選択は私の生活をより難しくする。ソート済みのリストや include も同様で、ソートしなければ常に末尾に追加することになり、マージコンフリクトが多発する。自動フォーマッタの利点と同じように、ソートでも時間を節約すべきだ。そして私は一貫しないスタイルに敏感で、1つのスタイルに統一することが最も重要だと思う。
    • 細かいオプションにはそこまでこだわらないが、共通の linter 設定で PR を見るときの無駄なノイズを減らすのは必須だと思う。git や各種 PL が行単位でしか扱わないのは洗練されていないと感じる。Ada 言語のようにもっと洗練された方式を使った例は、20年のキャリアで一度しか見たことがない。本当に優雅で効率的なものを作るのは難しく、すでに十分良い代替が多いならなおさら広まりにくい。
    • 私も若いころこの論争にハマったことがある。なぜかというと、自分がチームで一番賢いと勘違いしていたからだ。自分の意見が一番重要で、みんなが聞くべきだと思っていた。でも間違っていた。
  • ここで考えるべきトレードオフは、テキスト以外のフォーマットを使うと grep、diff、sed、バージョン管理のような汎用ツールの使い勝手が落ちることだ。結局、特殊なツールやフォーマット、IDE 拡張への依存度が上がる。Unix 哲学の強みは平文(Plain Text)による合成可能性にある。だからこの論争を簡単に断ち切る質問がある。エディタで先頭の空白幅だけ自由に設定できるとしたら、タブを好む人たちの論拠はまだ残るだろうか?
    • テキストベースのアプローチには利点があるが、私たちは少し下ってもっと高い山(projectional editing)へ行けたはずなのに、行けずに麓でさまよっていると感じる。挙げられた例はどれも構造情報のあるコードでよりうまく機能する。たとえばシンボル検索(テキスト grep よりずっと頻繁に使う)には ast-grep が使えるし、diff も semanticdiff.com のように構造的な移動や意味のない変更の無視をサポートできる。sed の代替としては @codemod/cli が使える。バージョン管理でも Unison のような言語で、非意味的変更(順序、空白など)によるコンフリクトを自動回避する実験が多く行われている。
    • このアイデアは何度も出てくるが、単純にフォーマッタを1回走らせる代わりに非常に複雑なツール群が必要になるという点で、費用対効果がよくない。すべての開発者が自分好みに見られること自体、実際には大きな意味はない。これまで一緒に働いたどのチームでも、皆でルールを決めてフォーマッタで強制適用していたが、最初は同意しなくてもすぐ慣れる。フォーマット論争は典型的な時間の無駄だ。
    • grep、diff、sed、行ベースのマージは、実のところコード操作には悪いツールだ。こちらの論争より、もっと良いツールを考えるべきだと思う。
    • 中間表現(Intermediate Representation)をテキストにしておけば、grep/diff/sed はすべて動作する。もし AST ベースのフォーマッタだけを使うなら、コードは特定の AST に合わせた正規化済みの形で保存され、エディタは AST をパースしてユーザーが望むフォーマットで表示し、保存時に正規化された形へ変換すればよい。
    • OS 全体がこうしたソースファイルへの依存を前提に作られてきた。Unix 哲学も、すべてのツールが平文テキストだけを想定していて、それをパースできてこそ本当に力を発揮する。
  • ソースコードのフォーマットにはタイポグラフィ的な要素も確かにあると思う。無条件に個人の好みだとみなす主張には同意しない。特定のフォーマットは意味と構造を効果的に伝える道具だ。自動化ツールで最小トークンだけを直列化して再構成すると、こうした価値は失われる。https://naildrivin5.com/blog/2013/05/17/source-code-typograp...
    • 印刷の専門家たちは、表や数式の間隔や整列のために長年心を砕いてきた。部外者にはわかりにくいが、こうした部分は非常に重要だった。ソースコードでも、もっと精巧にフォーマットしていく発展の余地があると思う。
    • たとえば Python の black formatter には、SQLAlchemy のクエリを過度に複数行へ分割した結果、かえって可読性が下がるという不満がある。
    • コードのタイポグラフィの重要性を、なぜ多くの人が感じ取れないのかいつも不思議に思っていた。
    • タイポグラフィに気を配りながら、一方でプログラミング言語の慣習には無批判に従うのは最悪の方向だと感じる。たとえば register のような単語は平気で使う文化がありながら、ポインタはアスタリスク(*)で表記する習慣があるなど、あらゆる表記はもっと直感的で明確にできるのに、あえて複雑で分かりにくい方法に固執している。記号や予約語も、もっと親しみやすく自然な用語に変えてよいのに、伝統的・既存の慣例にこだわりすぎて可読性を犠牲にしていると思う。例として C コードの strcpy 関数も、もっと明確で読みやすい用語と文法で完全に再構成できると説明している。
    • C における parameter 宣言が modifiers、データ型、名前で構成されることを説明したうえで、char argv[] のような例を使い、複雑な宣言の可読性の問題を突いている。そして C++ スタイルのフォーマット(例: char a, b)も誤解を招きかねないとして、そのようなスタイルは避けるべきだと考えている。
  • 私はこの論争の前提に同意しない。コードフォーマットは非常に重要なコミュニケーション手段だ。よいフォーマットは、開発者が (1) フォーマットの重要性を認識し、(2) ルールをよく守り、(3) 良いセンスと (4) 例外的な状況にも適切に対応する判断力を持っているというシグナルとして機能する。この4つは単なるフォーマット以上に、開発能力全般へ影響する重要な能力だ。ところが autoformatter や linter ルールは、こうしたシグナルを弱め、Goodhart の法則のように本来の趣旨を失わせる問題がある。
    • ブログの内容は短くて簡単なので、ぜひ直接読んでほしいと勧めている。タイトルだけ見て即座に反発せず、"should" と "unnecessary" という言葉の文脈を理解するとよいと思う。
    • フォーマットに無関心な人は、(1) 複数人で1つのファイルを編集した経験がないか、(2) ブランチのマージをしたことがないか、(3) 大規模コードベースの保守経験がないか、(4) 大規模リファクタリングの経験がないか、(5) コード履歴を見つけたり diff/比較ツールを活用した経験がないか、(6) コードベースの自動化ツールを開発した経験がないか、(7) 協業意識がなく自分のことしか考えないスタイルだと思う。
    • その項目すべてが「いいえ」なら、単に pass/commit 時にフォーマットを自動化し、自動化を通さなければ CI でエラーにすれば十分だと思う。細部に執着するより、ただデフォルトに合わせて個人のスタイルを捨てるのもむしろ利点だ。デフォルトのままなら、コードを見る誰にとっても馴染みやすく感じられる。しかもフォーマットと lint は別物だが、どちらも自動化ツールでまとめて解決できる。
    • 同じ評価はペン字にも当てはまると言い、冗談で今後は PR を筆記体で提出させるべきだというユーモアを添えている。
    • 「良いフォーマット規則を選ぶセンス」に依存するのは、むしろ論争を呼び時間を浪費しやすいと勧めている。Go や Rust のように組み込みフォーマッタに任せるほうがよい。
  • 構文木ベースでコードを保存し、人間が見るコードはそれをレンダリングしたものとして扱う試みは、20〜25年前から存在していた。最初に refactoring が流行した90年代にさかのぼる流れだ。Visual Age などの IDE は、ファイルシステムの代わりにデータベースへコードを保存するモデルを導入したことがある。intentional programming やモデルベース開発(モデル駆動開発)もこのサイクルの一部だ。refactoring の本質は AST(構文木)変換であり、シンボル名の変更は非常に簡単で、ソースコード上で直接検索置換する必要はない。だが人々はいまだにファイル編集に慣れていて、構造そのものを保存するアプローチが普及するには抵抗と摩擦がある。時間がたってもなおフォーマット論争が続いているのは、この代替案の必要性を示しているとも言える。言語やエディタのレベルで robust な symbol rename すらまともに提供できていない例は珍しくない。
    • 抽象化レイヤーの混同だという意見もある。AST も結局はファイルに保存される必要があるため、人間が読めるファイルに対して AST-aware なツールを走らせるのと大差ない。保存フォーマットはそれほど重要ではなく、核心はより賢いツールの問題だ。Microsoft の Roslyn のような事例や、現代のコンパイラが API を通じてコードベースと相互作用しようとしている方向性が例に挙げられている。
  • ある種のフォーマットは AST だけでは導き出せないことを、具体例で示している。たとえば複数の assignment があるコードで、3行を一直線にそろえるのか、= を基準に整列するのか、あるいはインデントを入れて構造の深さをより強調するのかではすべて異なる。値自体を強調したいなら数値を右寄せにし、構造を強調したいならメンバー変数をそろえて見やすくするなど、作者が強調したいコードの側面は異なりうる。こうした情報は追加のメタデータなしには AST から抽出できないと主張している。
    • 言いたいことは分かるが、実際に使うのは最初の2つの方式くらいだ。実際の目的は強調というより可読性だった。AST に変換する際に失われるものはあるが、この程度なら得られるもののほうがはるかに大きいと思う。しかも強調のための変形を AST に残す形で表現することも十分可能だ。たとえば setValue([bar, glob], 1) のような形や、スタイルオーバーライドのためのコメント構文など、さまざまに対応できる。
    • 「望ましいコードフォーマット」は結局主観的だ。例のように、2/4/8 スペース派もいれば、桁そろえを好む人もいる。AST はソースコードのフォーマット情報を持たないので、自動導出は不可能だ。
    • 2番目と3番目のフォーマット例は、実際には構造設計上の問題(Law of Demeter 違反)であって、フォーマットの範疇ではない。
  • Projectional Editing はテキストソースにも適用できる。JetBrains MPS でコードから表をレンダリングする例の動画もある。https://www.youtube.com/watch?v=XolJx4GfMmg&t=63s IDE で dictionary を表としてレンダリングしてくれる機能がほしい。現在でもコードフォールディング、インレイヒント、HTML としてレンダリングされた docstring など、その類似機能の一部は存在する。https://x.com/efortis/status/1922427544470438381
  • 私たちは plain text より抽象的な何かを今すぐ受け入れられない限界を持っている、と定義している。試みても結局 plain text のプロジェクションへとダウングレードされる。Morse Code から Unicode まで積み上がった「巨大なルックアップテーブル(GLUT)」が、現在の記号解読文化を生んだという見方だ。抽象化レベルを上げればアプリケーションにより適したシンボルセットを作れるが、それに対応するツールは出てこない。結局、ただのテキスト伝送へ変換してパースすることになる(CSV や Markdown など)。XML でも専用エディタが現れることはあるが、人々は結局 plain text で編集したがる。ただし文字エンコーディングの問題や特殊文字のせいで、完全に肯定的とも言えない。
  • 保存された artifact と、私たちが実際に見ているコードのプロジェクションが、なぜ同じである必要があるのかと時々疑問に思う。git diff ですら IR(中間表現)のプロジェクションとして確認できればよい。treesitter のような AST ツールの登場によって、人間が AST や IR をより効率的に扱うインターフェースを想像できるようになった。例として f# の ordered compilation 構造はコードレビューを単純化する助けになる。一方で自由な順序を許す言語や構造では、小さな diff を確認するだけでも何か所も行き来しながら変更の全体文脈を把握しなければならず面倒だ。
  • eslint-config-airbnb に関して不便だった点を共有している。代表的な issue は #1271#1122。既存プロジェクトに airbnb config を適用しようとして1時間以上無駄に格闘し、コードはもともと完璧だったのに不要なルールのせいで非生産的だったと感じた。結局そのルールだけローカルで無効にし、それ以降のプロジェクトでは二度と使わない。この例は、誤った lint ルールがどれほど生産性を損なうかを示している。