前回の最後に出てきた 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: 3 を retires: 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: 正常。注記や警告が付いていても 01: エラー。字句・静的・データ側のすべて2:nextで要求件数に届かなかった
これで CI が組める。祝日表を更新する PR に kairos list を 1 本走らせておけば、covering の外の日付も綴り違いも、レビュー前に赤くなる。残走路の監視は別の仕組みになる(第 5 弾)。
読み方のまとめ
- 先頭の
語:——どの操作で止まったか - 本文——何が悪いか
- 括弧の中——どう直すか。正しい式の形まで書いてあることが多い
- 末尾の
ADR-nn・§n.n——なぜそう決まっているか。 GitHub の design/20-adr/ に決定記録の全文がある - 止まる(エラー)・止まらないが言う(警告と注記)・機械に伝える(終了コード)の三段
▶ Playground でわざと間違えてみる——axis: を on: に直すと通る。
次回は、いよいよこの連載が追いかけてきた言語の、節目の報告になる予定である。
Kairos は設計中のスケジュール定義言語です。仕様と参照実装はGitHub で公開しています。

コメント