
2026/03/02 0:23
**リル・ファン・ランズ・ガッツ**
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
(以下は指定された本文の日本語訳です)
要約
本書では、複数層にわたる設計選択を明確に行うことで、コンパクトな関数型言語コンパイラを構築する方法を概説しています。
- 評価戦略 – 強制評価(strict)と遅延評価(lazy)の違い、およびそれらが引数の評価とキャッシュに与える影響。
- 正規化形式 – 式を平坦化するための ANF/K‑normal 形式、遅延評価用の STG/thunks など。
- クロージャ変換 – 平面クロージャ、リンク/共有クロージャ、ラムダリフティング、デファンクショナライズ、または組み合わせ論理(例:Ben Lynn の MicroHs)。
- コード生成ターゲット – ネイティブアセンブリ、C ソース、LLVM IR、Cranelift、バイトコード VM、JavaScript/Wasm など。各選択肢は性能と複雑さのトレードオフを伴います。
- ランタイムシステム – 小規模なエントリポイント、スタックセットアップ、GC(Cheney コピー、マーク・アンド・スイープ、参照カウント、領域ベース)、プリミティブ、および割り当てルーチン。
- 型推論コア – Hindley–Milner Algorithm W/J。オプション拡張(型クラス、GADT、依存型など)を加えると LOC が大幅に増加します。
- パターンマッチコンパイル – 決定木、バックトラッキングオートマタ、入れ子の if/switch。網羅性/冗長性チェック付き。
- 最適化パス – ベータリダクション(約 50 LOC)、let 平坦化(約 22 LOC)、インライン展開(約 100 LOC)、定数畳み込み(約 50 LOC)、未使用コード除去(約 50 LOC)。
- ブートストラップ vs ホステッド – ブートストラップ型コンパイラはターゲット言語で自分自身を書き、ホステッド型は既存のホスティングエコシステムに依存します。
- 型システム形態 – 名義型(OCaml、Haskell)と構造体型(EYG、Grace、TypeScript)の違い。これが型等価性に影響します。
- エラーレポーティング品質 – 「きれいな」エラーにはすべての AST ノードで完全なソーススパンが必要であり、デシュガリングと推論を通じて保持されます。
本文はまた、将来の作業としてより豊富な型機能、完全なソーススパン付きの改善されたエラーメッセージ、および LLVM IR や WebAssembly など新しいランタイムターゲットの探索を示唆しています。これらの選択は総合的にコンパイラの性能・メモリフットプリント・実際のプロジェクトで言語をデプロイする開発者への使いやすさを形作ります。
本文
「リル・ファン」言語がどう動くか
1. コア特徴
| Feature | Typical values |
|---|---|
| 評価戦略 | strict vs. lazy |
| カリー化 | curried (Haskell) vs. bland (OCaml, MinCaml) |
| ブートストラップ方式 | hosted (C/OCaml などで書かれたもの) vs. bootstrapped (自分自身がホストする) |
| 実行モデル | インタプリタ、バイトコード VM、ネイティブコンパイル、トランスパイル |
| 型システム | nominal vs. structural types |
| エラー報告 | ugly (プレーンテキスト) vs. pretty (ハイライト付きソース+解説) |
2. 一般的なコンパイラパイプライン
Lexing → Parsing → Desugaring → Type‑Inference → Pattern‑Match Compilation → Normalization → Optimization → Closure Conversion → Code Generation → Register Allocation ↘ Runtime System (GC, primitives)
- Strict evaluation:関数呼び出し前に引数を評価する。
例
はlength [1, foo 2, 4]
を強制的に評価します。foo 2 - Lazy evaluation:必要になったときだけ引数を評価し、結果はキャッシュされます。
3. 評価モデル
| Strict | Lazy |
|---|---|
| ML, OCaml | Haskell, MicroHs (STG経由) |
Strict はシンプルで、Lazy は無限データ構造を可能にします。
4. カリー化 vs. ブランド関数
-
カリード (
→f x y
)((f) x) y- 各中間クロージャーはヒープ割り当てが必要になる(アリティ解析で固定引数数を検知できない限り)。
- 例:Haskell、MicroHs (combinator backend)。*
-
ブランド (
)let f = fun (x,y) -> …- 引数ごとのクロージャーは不要。関数はタプルや複数パラメータを取ります。
- 例:MinCaml、OCaml(内部実装)、Grace。
5. ブートストラップ vs. ホステッドコンパイラ
| Approach | Example | Notes |
|---|---|---|
| Hosted | MinCaml (Rust/C) | ホストエコシステムを利用して構文解析・ビルドツールを扱う。 |
| Bootstrapped | MicroHs、Ben Lynn のチェーン | コンパイラ自体がその言語で書かれ、各段階でより大きなサブセットをコンパイルする。 |
ブートストラップは自己完結型コンパイラと堅牢なテストスイートを提供します。
6. インタプリタ vs. コンパイラ
| Strategy | Typical LOC | Trade‑offs |
|---|---|---|
| Tree‑walking interpreter | 50–200 | コード生成が不要で実行速度は遅い(10〜100×ネイティブ)。 |
| Bytecode VM | 200–500 | ポータブルだがやや遅く、カスタムバイトコードを必要とする。 |
| Native compilation | 500–1500 | 最速だがレジスタ割り当て・ABI処理が必須。 |
| Transpile to C/LLVM | 200–500 | 成熟したバックエンドを活用し、良好な性能を得られる。 |
インタプリタはクロージャ変換とレジスタ割り当てもスキップします。
7. 型システム
Nominal vs. Structural
type Meters = Int -- nominal: Meters ≠ Seconds type Seconds = Int -- structural: Meters = Seconds
| System | Examples | Consequence |
|---|---|---|
| Nominal | OCaml, Haskell | 同じ形状でも型が異なる。 |
| Structural | EYG, Grace, TypeScript | フィールドやバリアントが同じなら型は同一とみなされる。 |
8. エラー報告
| Style | Example |
|---|---|
| Ugly | |
| Pretty |
3 │ let x = 1 + "hello" │ ^^^^^^^^ Error: I expected an `int` here, but got a `string`. The left side of `+` is `int`, so the right side must be too. ``` | *Pretty エラーは全段階でソース位置情報をスレッドする必要があります。* --- ### 9. レキシング | Approach | Used by | LOC | Notes | |----------|---------|-----|-------| | 手書き再帰 | MinCaml (Rust), Tao, Ante | 100–300 | 完全な制御が取れる。 | | ocamllex / mlllex | MinCaml (original), HaMLet | 50–100 | OCaml ホストで標準的。 | | Alex (Haskell) | MicroHs、他多数の Haskell ホスト | 50–100 | Haskell ホストで標準。 | *オプション: レイアウト/インデント感知、Unicode 識別子、挿入文字列など。* --- ### 10. パーシング | Approach | Used by | LOC | Notes | |----------|---------|-----|-------| | 再帰下降 + Pratt | MinCaml (Rust), Tao, Ante | 200–500 | 良好なエラーメッセージ。 | | ocamlyacc / mlyacc | MinCaml (original) | 100–200 | LALR 文法ファイル。 | | パーサコンビネータ | Ben Lynn、MicroHs、PLZoo | 100–400 | エレガントで合成的。 | --- ### 11. 型推論 *標準アルゴリズム:Hindley‑Milner (Algorithm W or J)。 典型 LOC: 100–400.* | Feature | Added LOC | |---------|-----------| | Type classes / traits | +500–2000 | | Row polymorphism | +300–800 | | Higher‑kinded types | +200–500 | | GADTs | +500–1500 | | Dependent types | +1000–5000 | --- ### 12. パターンマッチコンパイル | Strategy | Used by | LOC | Notes | |----------|---------|-----|-------| | Decision trees (Maranget) | Tao, Ante | 200–600 | 最適なテスト。 | | Backtracking automata | 古いコンパイラ | 100–300 | 単純だが重複作業が多い。 | | Nested if/switch | 教育用コンパイラ | 50–100 | 悪いケースで指数的に増える可能性。 | --- ### 13. 正規化 | Strategy | Used by | Notes | |----------|---------|-------| | K‑normal / ANF | MinCaml、他多くのモダンコンパイラ | 明示的な let チェーンを生成し、後続パスを簡素化。 | | CPS | Appel’s SML/NJ、Rabbit | 継続を導入して tail call を実現。 | | No normalization | Ben Lynn | 直接 combinatory logic で動作。 | --- ### 14. 最適化(MinCaml の典型セット) | Pass | Rough LOC | Effect | |------|-----------|--------| | Beta reduction | ~50 | `let x = y` → `y` をインライン化。 | | Let flattening | 22 | ネストした let を結合。 | | Inline expansion | ~100 | 小さな関数呼び出しを本体に置き換え。 | | Constant folding | ~50 | `3 + 4` を計算。 | | Dead code elimination | ~50 | 未使用のバインディングを除去。 | *追加最適化(tail‑call, unboxing, deforestation 等)で LOC は増加します。* --- ### 15. クロージャ変換 | Approach | Used by | Trade‑offs | |----------|---------|------------| | Flat closures | MinCaml、OCaml | 各クロージャはヒープ割り当てを行うがアクセスは O(1)。 | | Linked/shared closures | 古い Scheme コンパイラ | 構造体を共有するがアクセスが遅くなる。 | | Lambda lifting | GHC(選択的) | クロージャ自体のヒープ割り当てを省き、呼び出し側に追加引数を渡す。 | | Defunctionalization | MLton | 関数ポインタを除去するが全プログラム解析が必要。 | | Combinatory logic | Ben Lynn、MicroHs | クロージャを一切使用せず、グラフリダクションで実行。 | --- ### 16. コード生成 | Target | Used by | LOC | Trade‑offs | |--------|---------|-----|------------| | ネイティブアセンブリ | MinCaml、mlml | 300–800 | 性能最高だがプラットフォーム固有。 | | C ソース | Koka、Scrapscript | 200–500 | ポータブルだが C コンパイラに依存。 | | LLVM IR | Ante、gocaml | 200–500 | クロスプラットフォーム性能は良好だが重い依存。 | | Cranelift | MinCaml (Rust port) | 200–500 | コンパイル時間を短縮。 | | バイトコード VM | OCaml (ZINC)、PLZoo | 200–500 | 実装は簡単だが実行速度は遅い。 | --- ### 17. レジスタ割り当て *ネイティブバックエンドにのみ必要。 代表的手法:Chaitin‑Briggs のグラフ彩色、線形スキャン。* --- ### 18. ランタイムシステム | Component | Typical LOC | Notes | |-----------|-------------|-------| | Entry point / スタックセットアップ | 10–30 | 最小限の C コード。 | | ガベージコレクター | 100–1000 | Cheney コピー、マーク&スイープ等。 | | プリミティブ操作 | 50–200 | I/O、数値計算、文字列処理など。 | | 割り当てルーチン | 10–50 | GC がコレクションを行う場合はバンプアロケータ。 | *GC 戦略:コピー(Cheney)、マーク&スイープ、参照カウント、アリーナ/スタック、領域ベースなど。* --- ### 19. 要約 * **インタプリタ** は多くの段階を省き、軽量化(≈500 LOC)です。 * **コンパイラ** は最適化・コード生成・ランタイムでさらに 2000–4000 LOC を追加します。 * 評価モデル、型システム、バックエンドにより必要な作業量が決まります。 この短縮ガイドは、小規模関数型言語プロジェクトでよく見られる設計判断と実装トレードオフを網羅しています。