Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

付録E Lune の設計と、これから

本文はずっと「どう書くか」の話でした。この付録は「なぜそう決めたか」の話です。 言語の設計には、たいてい引き受けた不便があります。それを隠さずに置いておくのが、 この本のいちばん最後の仕事です。

E.1 なぜデフォルト遅延なのか

遅延評価は初学者の罠だ、というのは正しい批判です。 いつ計算が走るのか見えないと、 プログラムの動きが読めません。副作用の順番も直感を外れます。教育向けの言語 (Racket、Pyret、Hedy)がすべて正格なのは、偶然ではありません。

それでも Lune はデフォルト遅延を選び、そしてやめないと決めています。理由は 「遅延が便利だから」ではありません。

批判への答えが「隠す」ではなく「見えるようにする」だったからです。

隠す道Lune が選んだ道
評価のタイミング見えないので考えない:thunks で状態を、:trace で瞬間を見る
循環した定義固まる(Haskell の <<loop>>)RUN0005 を即座に、解説付きで報告する
正格にしたいとき言語が勝手に決めるstrict / ! / seq / force / deepForce で自分が決める

第4章がまるごとこの答えです。「信じる必要はありません。すべてその場で観察できます」と 書いたのは、可観測性が設計の代償ではなく設計の中身だからです。

ここには狙いがもう一つあります。正格な言語で「評価戦略」を教えるのは難しい — 教える対象がプログラムの外側にあるからです。デフォルト遅延の言語では、評価戦略が プログラムの内側の観察対象になります。tick() を2回呼んでカウンタが 0 のままなのを 見た瞬間(付録B)、遅延は概念ではなく現象になります。

設計の緊張 — 「初学者に優しい」と「評価戦略を教えられる」は、普通は両立しません。 Lune はこの緊張を、診断と可観測化で解こうとしている言語です。うまく解けているかは、 あなたが第4章を読み終えたときの感触が答えです。詳しい戦略は documents/STRATEGY.md の施策 D・E にあります。

E.2 なぜ null と Option の両方があるのか

第5章で Option[T]、第7章で T? を学びました。「ないかもしれない」を表す道具が 2つある、というのは設計としては贅肉に見えます。

そう見えて、どちらも消せませんでした。

Option は消せません。 これは言語機能ではなく、ADT とパターンマッチがあれば 誰でも書ける型です。実際 prelude の中で type Option[T] = | Some(value: T) | None として定義されているだけで、コンパイラは特別扱いしていません。ADT を持つ言語から Option を取り上げることはできない —— 禁止しない限り誰かが定義します。

T? も消せません。 Lune の長期的な目標には JVM 上で動き、Java のライブラリを直接 呼ぶことが入っています(documents/LANGUAGE_FUTURE_SPEC.md)。そして Java の参照は すべて null になり得ます。T? は将来仕様の Java 型対応表で nullable reference に対応する型で、つまり外の世界と話すための型です。null のない言語は、null のある 世界と話せません。

だから Lune は null を廃止せず、型で飼いならす道を選びました(第7章)。 「null を消した言語」ではなく「null が入りうる場所を型に書かせ、確かめる前に使わせない 言語」です。

役割は自然に分かれます。

T?Option[T]
出自言語機能(専用構文 ?? ?. と絞り込み)prelude の ADT
向いている場所データの形。レコードのフィールド、関数の引数と戻り値リスト処理と高階関数のパイプライン
外との境界Java の nullable 参照に対応するLune の内側の話

実用の目安は第7章 7.7 節にあります。境界では match で詰め替えれば行き来できます。

正直に言うと — 2つあることの代償は、読者が使い分けを覚えなければならない、という 一点です。これは実際に負担です。7.7 節に判断の目安を置いたのは、その負担をせめて 「表を見れば済む」大きさに抑えるためです。

E.3 v0.1 が引き受けた制限

何を作らなかったかは、何を作ったかと同じくらい設計です。使えないものの一覧と、 出る診断は付録A の A.6 にあります。ここではなぜの側を書きます。

凍結したもの — JVM と OO

class / interface / extends、Java ライブラリの直接呼び出し、JVM バイトコード生成。 これらは将来仕様(LANGUAGE_FUTURE_SPEC.md の Phase 2〜4)にありますが、当面凍結 という判断が明示されています(documents/STRATEGY.md §5)。

理由は能力ではなく戦線の選択です。Java 相互運用は「実務で採用してもらう」ための施策で、 そこは勝ち目の薄い戦場です。一方、教育という用途には Python インタプリタの速度で足ります。 だから Lune は「実務言語になる」道を降りて、「母語で教えてくれるコンパイラを持つ 関数型入門言語」に集中しています。

class や new を書くと構文エラーになるのに、キーワードとしては予約されている — という中途半端な状態は、この凍結の跡です。将来の席は空けたまま、今は座らせていません。

まだ無いもの — try / catch

失敗を例外で飛ばす道具がありません。これは凍結というより順番の話です。第5章の Result、第7章の T? という「失敗を値として返す」道具を先に据えたので、例外は それらと役割が重なります。第9章で「回復可能な失敗は値で表してください、raise の出番は プログラムを止めるのが正しい場面だけです」と書いたのは、この設計の帰結です。

小さく欠けているもの

break / continue(ループ条件に織り込む — 9.3 節)、record update と record pattern、 mutable な record フィールド、ADT のフィールドアクセス、Stream / Map / Set。 文字型もありません(付録A の A.1)。

このあたりは思想ではなく、まだ手が回っていないだけのものが混じっています。付録A の A.6 が「何が起きるか」の一覧、LANGUAGE_FUTURE_SPEC.md が「どうしたいか」の一覧です。

E.4 処理系を読みたくなったら

Lune の処理系は lune/ 以下の Pure Python、約 7,400 行、外部依存なしです。 1日で読み切れる規模なので、言語の実装を初めて読む題材に向いています。

ソースは検査の順番に並んでいます。付録A の A.1 で見た診断コードの族(LXL → LAY → PRS → MOD → TYP → RUN)が、そのままファイルの並びです。

ファイル行数役割
lexer.py294文字列 → トークン列。LXL
layout.py61インデント → INDENT/DEDENT。LAY
tokens.py214トークン種別とキーワード表
parser.py763トークン列 → AST(Pratt パーサ)。PRS
nodes.py405AST の定義。ここを読むと言語の全体像が分かる
module_loader.py188import の解決。MOD
typechecker.py1591型検査と網羅性。TYP・REC。最大のファイル
evaluator.py1259遅延評価とサンク。RUN
diagnostics.py124診断の表示形式(caret や hint の組み立て)
messages.py332日英メッセージカタログ
explanations.py / explanations_ja.py530 / 428lune explain の教材カタログ
formatter.py / fixer.py490 / 66lune fmt / lune fix
repl.py347REPL。Playground もこの ReplSession を駆動している
cli.py269コマンドライン入口

読む順のおすすめ: nodes.py → lexer.py → parser.py → evaluator.py。 最初に AST を見ておくと、あとは全部「AST をどう作るか / どう扱うか」の話になります。 遅延評価に興味があるなら evaluator.py の Thunk と force_value から入るのが最短です。

仕様書の読み方

documents/ に仕様書が並んでいます。LANGUAGE_SPEC.md が v0.1 の正で、そこから 各論に降りる構造です。

知りたいこと読む文書
v0.1 で何が使えるかLANGUAGE_SPEC.md(これが正)
表面構文・EBNFSYNTAX_SPEC.md / LEXER_PARSER_SPEC.md
遅延評価の規則LAZY_EVALUATION_SPEC.md
型検査の範囲TYPE_CHECKER_SPEC.md / MATCH_EXHAUSTIVENESS_SPEC.md
診断の設計ERROR_DIAGNOSTICS_SPEC.md / ERROR_INDEX_JA.md
どこへ向かうのかLANGUAGE_FUTURE_SPEC.md / STRATEGY.md

仕様書を読むときの注意 — 仕様書は「正」とされていますが、v0.1 の実装が追いついて いない箇所や、逆に実装のほうが正しい箇所があります。この本の付録A を書く途中でも 2件見つかりました(LXL0004 が出せなかった件と、文字型の件)。疑ったら ./bin/lune に通して確かめてください。 それが一次情報です。この本の付録は全部 そうやって書きました。

E.5 これから

documents/STRATEGY.md が向かう先を書いています。要約すると、汎用普及は狙わず 「母語で教えてくれるコンパイラを持つ関数型入門言語」に集中し、JVM / OO 路線は凍結する。 デフォルト遅延は隠すべき弱点ではなく、前面に出す差別化点として扱う。

この本を読み終えたあなたにできることが、いくつかあります。

  • つまずいた場所を教えてください。 どの診断で詰まったか、どの説明が足りなかったかは、 そのまま言語の改善点です。診断が不親切だったなら、それは言語のバグだと考えています。
  • 診断を増やす。 汎用の RUN0006 に集約されている実行時エラーには、専用コードに 昇格させる余地があります(STRATEGY.md 施策 E の作業項目)。
  • 書いてみる。 v0.1 でも第13章のようなプログラムは書けます。足りない道具に気づいたら、 それが次に作るものです。

部訓をもう一度。必要になるまで、やらない。一度やったことは、忘れない。 遅延評価とメモ化のことですが、言語の作り方の話でもあります。