そのエラーには直し方が書いてある——診断メッセージの読み方

Kairos

前回の最後に出てきた 1 行

前回の終わりに、こういうエラーが出てきた:

shift: 点が軸上にない(先に roll で有効点へ寄せる)

この 1 行は、三つの部品でできている。先頭の shift:どの操作で止まったか。続く「点が軸上にない」は何が悪いか。括弧の中はどう直すか。Kairos の診断メッセージは、ほぼすべてがこの三部構成で書かれていて、ものによっては四つ目の部品——末尾の ADR-39§4.7 といったなぜそう決まっているかの参照——が付く。

エラーは「止まった」という事実より、何を言っているかの方に価値がある。今日はその読み方を一度きちんと書いておく。例はすべて前回と同じ前提(東京・2026 年秋の祝日を宣言した JP)で、参照実装の CLI(kairos list --from 2026-09-01 --to 2027-01-01 定義ファイル)で実測したものである。

止まる 1: 字句エラー——「どこ」を言う

いちばん手前で止まるのが字句エラー。日本語環境で起きやすいのがこれ:

bizDay = everyDay ¥ (satSun | national)
#=> 字句エラー(5:21): 円記号 ¥(U+00A5)は差演算子ではない。バックスラッシュ U+005C を使う(§4.5)

\(差演算子——everyDay から土日祝を除く)を打ったつもりが、¥ になっている。フォントによっては画面上で見分けがつかないので、文字コードまで書いて止まる。先頭の (5:21) は5 行目 21 桁——字句エラーの仕事は「どこ」を言うことで、行と桁が必ず付く。

もう一つ、暦としてあり得ない日付:

national = [2026-09-21, 2026-09-22, 2026-09-31, ...] covering: ...
#=> 字句エラー(3:39): 実在しない日付: 2026-09-31(月は 01..12・日は月と閏年規則の実在日のみ——proleptic Gregorian 固定。F66/ADR-43)

9 月 31 日は読めた瞬間に止まる。意味の話(この祝日表が正しいか)は次の段の仕事で、字句の段は「文字として成立しているか」だけを見る。

止まる 2: 静的エラー——定義の意味が閉じない

文字としては読めるが、意味が定まらないとき。いちばん多いのは綴り違いである:

evryDay |> within(month) |> nth(25) |> roll(Preceding, on: bizDay)
#=> 未解決の名前: evryDay(premise 相対解決 §3.4)

roll(Preceeding, ...) と e を一つ多く書いても同じ形で止まる(未解決の名前: Preceeding)。ここまでは、どの言語でもそうだろう。Kairos らしいのはここから。

everyDay |> within(month) |> nth(25) |> roll(Preceding, axis: bizDay)
#=> roll: 未知の名前付き引数 axis:(黙って捨てない——ADR-39)

on: と書くべきところを axis: と書いた。地味だが、これは大事な性質である。設定ファイルを読む側の多くは、知らないキーを黙って無視する——retries: 3retires: 3 と書いても何も言われず、再試行 0 回のまま本番に行く。Kairos は知らない引数を受理しない。前々回に書いた「多義は受理しない」の隣に、「知らないものは受理しない」が並んでいる。

everyDay |> within(month) |> nth(25) |> roll(Preceding)
#=> roll: 軸がない(on:/unit: を書くか前文で axis: を宣言。§3.3 は宣言必須)

「どの軸へ寄せるか」を既定で埋めない。営業日かもしれないし、別の何かかもしれない——それは書き手が言うことで、直し方が 2 通り(その場で on: を書く/前文で axis: を宣言する)書いてある。

everyDay |> take(3)
#=> take: from: が必須(起点の明示。ADR-31・§4.7)

「最初の 3 つ」の「最初」はいつからか。「実行した日から」では、実行のたびに答えが変わる。起点を書けば通る——bizDay |> take(3, from: 2026-09-01) は 9/1・9/2・9/3 を返す。

そして、誘導が正しい式の形まで書いてあるもの:

everyDay |> within(month) |> take(3, from: 2026-09-01)
#=> take: 窓付き入力は取らない——「窓ごとの先頭 N」は within の後の nth(第 N)か ordinalIn の述語で(take は通し数え。ADR-49)

「毎月最初の営業日」を書きたくて take に手が伸びるのは自然である。エラーは「take は通し数え(月ごとに数え直さない)」と理由を言い、月ごとに数えるなら nth、と正しい語を指す。言われたとおりに書き換えると:

bizDay |> within(month) |> nth(1)
#=> 2026-09-01  2026-10-01  2026-11-02  2026-12-01

11 月が 2 日なのは、11/1 が日曜だから。

静的エラーにはもう一つ、覚えておきたい性質がある。点を 1 つも出さない。定義ファイルに式が3 つあって 3 つ目だけが誤っていても、1 つ目・2 つ目の結果は出ない(実測——正しい式の下に誤った式を足すと、出力はエラー 1 行だけになる)。「半分だけ動く」が無い。定義ファイルは全体で一つの契約である。

止まる 3: データ側のエラー——式は正しいのに止まる

式に間違いがなくても、データが契約を破ると止まる。

national = [..., 2026-11-23, 2027-05-03] covering: 2026-08-01..2027-01-31
#=> テーブルの要素が covering の外: 2027-05-03(列の全要素は covering に包含——ADR-37)

「この表は 2027 年 1 月末まで確認済み」と主張しながら、5 月の祝日が入っている。主張と中身の矛盾で、第 5 弾で書いた「鮮度」の裏面にあたる。

national = external(kind: dates, source: "jp-holidays")
#=> 供給エラー: 解決子がない——external JP.national(source: "jp-holidays")は解決できない(ADR-46 判断 7 (a))

第 14 弾の「式は静的、データは実行時」で、データが来なかった日の姿である。祝日表が届いていないのに「祝日なしとして計算」はしない。

premise NY { calendar-system: Gregorian; tz: "America/New_York"; wkst: Mon }
@NY
everyInstant |> strideBy(1d, from: 2026-03-08T02:30)
#=> 存在しない時刻: 2026-03-08T02:30(tz "America/New_York" の DST の隙間に落ちる——実在の壁時計で書く。ADR-33)

第 7 弾の春の 1 時間。ニューヨークの 2026 年 3 月 8 日には 2:30 が存在しない(2:00 の次が 3:00)。多くの実装はここで黙って前後にずらすが、Kairos は「その壁時計は実在しない」と言って止まる。

止まらないが、言う——警告と端の注記

すべてが止まるわけではない。定義は正しいが、この実行では全部は見えない——というときは、結果を出したうえで添え書きが付く。

everyDay |> within(month) |> nth(25) |> roll(Preceding, on: bizDay)
# --to 2027-04-01 で実行
2026-09-25
2026-10-23
2026-11-25
2026-12-25
2027-01-25
2027-02-25
2027-03-25
# ⚠ 範囲外 2027-02-01..2027-04-01(JP.national covering 2026-08-01..2027-01-31)
# 被覆サマリ
#   JP.national covering 2026-08-01..2027-01-31 残走路 -59 日

2027 年 2 月以降の点は出る。ただし「祝日表はその先を知らない」と添えて。残走路がマイナスなのは「表の更新が 59 日遅れている」という読み方で、前々回の三か条の二番目——データの端では言う——がここに現れている。

national
# kairos next -n 10 で実行
2026-09-21  2026-09-22  2026-09-23  2026-10-12  2026-11-03  2026-11-23
⚠ 地平線 10 年以内の発火は 6 件(要求 10 件)

「次の 10 件」を頼んで 6 件しか無かった。有限の表なのだから当然だが、それを黙らずに言う。

警告: horizon-clip: shift 2027-10-25(計算範囲 to+400日 の実体化地平線——言語の地平線ではない。ADR-37 判断 8)

これは shift(14, unit: month) のように、着地が計算範囲のずっと先に飛ぶ式で出る。捨てた点を列挙したうえで、「言語の地平線ではない」——定義の意味ではなく、計算範囲という道具の都合である——と文言自身が断っている。

線の引き方はこうである。エラーは言語の規則(定義が壊れている)。警告と注記は道具や環境の都合(定義は正しいが、この実行では全部は見えない)。前者は止まり、後者は出したうえで言う。

終了コード——人が読まなくても、機械が読む

メッセージは人が読むが、終了コードは機械が読む。

  • 0: 正常。注記や警告が付いていても 0
  • 1: エラー。字句・静的・データ側のすべて
  • 2: next で要求件数に届かなかった

これで CI が組める。祝日表を更新する PR に kairos list を 1 本走らせておけば、covering の外の日付も綴り違いも、レビュー前に赤くなる。残走路の監視は別の仕組みになる(第 5 弾)。

読み方のまとめ

  • 先頭の 語:——どの操作で止まったか
  • 本文——何が悪いか
  • 括弧の中——どう直すか。正しい式の形まで書いてあることが多い
  • 末尾の ADR-nn§n.n——なぜそう決まっているか。 GitHub の design/20-adr/ に決定記録の全文がある
  • 止まる(エラー)・止まらないが言う(警告と注記)・機械に伝える(終了コード)の三段

▶ Playground でわざと間違えてみる——axis:on: に直すと通る。

次回は、いよいよこの連載が追いかけてきた言語の、節目の報告になる予定である。


Kairos は設計中のスケジュール定義言語です。仕様と参照実装はGitHub で公開しています。

コメント

タイトルとURLをコピーしました