ADR-36: 結合子・軸所属の粒度整合——整列の静的検査
判断: 点の等値で所属を判定する演算——結合子 &・\(および | の一部規則)と軸所属
(filter(on:)・roll(on:)・shift(unit: 点列軸)・stride(n))——に、整列(alignment)の静的
検査を規定する(F56。F54 の詳細規則と ADR-33 判断 10 の残点=tz 等値の時期もここで確定する)。
- 意味論は変えない: 点の同一性は chronos 上の等値(ADR-33 判断 7)であり、結合子は点集合の
演算のまま。本 ADR が足すのは統治(どの二流の突合を正当と認めるか)だけである。
整列は式の導出構造に付く保守近似であり、同じ外延を表す二式で検査の可否が割れうる
(
snapToの明示でいつでも合流できる)——外延の意味論は不変。従来この検査が無かった下で 合法(だが黙って空振り)だった式は静的エラーに変わる: 挙動が変わるのは誤結果だけだが、 破壊的変更として記録する(ADR-16 の基準では欠陥の修正)。「正当な空」(ADR-15)とは別物—— 同一整列の突合が空になるのは従来どおり正当な値である。 -
整列とは: 時間ストリームの静的性質で、「全点が、ある原子グリッド G の目盛り点上にある」 という主張。G の正体は三つ組——幅(
1d等)・正規化位相(既定整列・anchor:・紀元を幅を 法として正規化した値。判断 3)・tz 名(市民時幅のとき。経過時間幅は位相が幾何を全て持つので tz を持たない)。整列の値は G か「なし」の二値で、式の導出構造から静的に計算される:窓語の要素グリッドとは、その窓連鎖の原子 grid を指す(
month・year・quarter・weekならdayの grid——span/splitは要素グリッドを保存して窓を組むため、公開窓語由来のストリームの点は 常に原子 grid の目盛り上にある)。segmentBy 由来の窓の「要素グリッド」はマーカーの整列で読む (窓の切れ目=マーカー点。snap 済みマーカーなら原子 grid と一致し、未 snap の瞬間マーカーなら 「なし」——snapTo(その窓)が原子 grid を主張しない安全側になる)。 単点の日付リテラルは表の対象外=主張なし(単発の除外は[2026-01-05]とテーブルで書く。保守近似・ 意味論不変)。式の形 整列 everyDay・公開窓語由来のストリーム(month \|> first等)要素グリッド( Gregorianならday = chronos grid 1dの正体)テーブルリテラル(全要素が日付のみの字句) 市民日グリッド。tz 成分=そのリテラルの錨打ちに実際に使われた tz(ADR-33 判断 8 の在圏解決と同一物——タグと点の幾何を乖離させない。実体・データ premise は tz:必須+member 解決規則〈ADR-35 判断 8〉により定義側の tz に収束する)テーブルリテラル(時刻付き要素、または字句でない要素〈計算値・名前参照〉を含む) なし strideBy(w, from: p)由来(everyInstantの実体化を含む)anchor 付きグリッド(幅 w・anchorp)——出力は定義上この目盛りそのものであり、「なし」に落とすと G の体系と不整合になるfilter(両形)・選択子・within・segmentBy・stride入力の整列を保存 roll(conv, on: A)軸 A の整列(出力点は A 上の点) shift(n, unit: 窓語 U)入力整列=U の要素グリッドなら保存、さもなくば なし(検査なし・下記) shift(n, unit: 点列軸 A)判断 3 の検査に合格した共通 G(=軸 A の整列。roll と対) snapTo(w)w の要素グリッドを主張(=再整列の明示手段。判断 5) A \| B両辺同一ならそれ、さもなくば なし(判断 4) A & B・A \ B共通整列(判断 3 で同一が保証される) 「窓語」と「点列軸」の区別は値の種別で決まる(規準は既存の意味論のまま): 軸位置の名前が 窓(パーティション)に解決されれば窓語(
shiftは窓の添字移動+窓内オフセット保存)、 時間ストリーム(点列)に解決されれば点列軸(shiftは軸上の歩進——軸外の点はエラー)。糖衣は core への展開後(機械的差し込み・ADR-23)・実引数の束縛後に計算する——仮引数 (
businessDays(on: p) = filter(on: p)のp)は定義体単独では不定だがエラーにせず、呼び出しごとに 展開先で判定する。premise 束縛越し(実体のnonWorking等)は定義側の文脈で計算した整列がそのまま 運ばれる。整列の tz 名成分は遅延解決の解決後に確定する(day = chronos grid 1dの tz は利用側 前文で決まる——wkst と同型・ADR-17)。ゆえに「静的」とは前文・名前解決の後、データ評価の前の 層を指す(在圏 premise の実体化ごとに検査が走る)。 -
検査:
&・\・filter(on:)・roll(on:)・shift(unit: 点列軸)は、両辺(入力と軸)の整列が 「同一の G」であることを要求する。どちらかが「なし」でも、G 不一致でも静的エラー。shiftの検査は unit が点列軸(軸ストリーム)のときに限る——窓語 unit(shift(+87, unit: day))は 等値所属でなく区間所属(属する窓の添字を動かし窓内オフセットを保存)なので判断 7 の免除系に 準じ、整列なしの入力も合法(節気の瞬間を +87 日して八十八夜を得る確定例がこの形)。stride(n)の軸相対カウント(在圏axis:・on:上書き)も同じ検査を受ける——数える単位(軸)と 数えられる列(入力)の整列がずれると「3 営業日ごと」が黙って別の間隔にずれるためである。 細分・包含による自動整合はしない——「時刻付き列と日次列は hour で見れば揃っている」式の 緩い判定を許すと、意図の単位(日)での突合が黙って空振りする反例(後述)が通ってしまう。G の同一=幅の同一・正規化位相の同一・(市民時幅なら)tz 名の等値。位相は anchor・紀元を 幅を法として正規化して比べる——経過時間幅は「anchor(無ければ紀元。
epoch:上書きを含める)の chronos 点 mod 幅」、市民時幅は「anchor の属する市民日の序数 mod 日数幅」と「anchor の日内 オフセット(壁時計)」の組。既定整列の日内オフセットは 0 なので、時刻付き anchor のグリッド (毎日 09:00 級)は既定のdayと別 G になる——日内オフセットを落とすと「毎日 09:00 と everyDay の&」が同一判定で素通りし黙って空になる(検査が防ぐはずの形)。既定整列は正規位相の一値として同じ土俵で比較される(市民時幅=真夜中位相・ 経過時間幅=紀元位相)——chronos grid 1d anchor: 2026-01-01と既定整列のdayは同一 G (目盛りは点集合として同じ)。anchor の chronos 点そのものの等値を要求する案は、等価なグリッド 同士(anchor が幅の整数倍だけ違う定義)を安全側エラーで割るため採らない。紀元を落とす案は、epoch:の異なる premise 間の同幅グリッドを同一と誤判定し、検査が防ぐはずの空振りを検査自身が 通すため採らない。premise を跨いでも成立する(同じGregorian系のday・同じ tz 名なら別 premise 由来でも同一)。F54(実体と利用側の tz 不一致)は tz 名の不一致としてここで検出される。 図解(2026-07-13 追補・判断 2〜5 の全体像——判断内容の変更なし。spec §4.5 と同図):flowchart TD OP["S₁ ⊕ S₂(⊕ = &・差・filter(on:)・roll(on:)・shift(unit: 点列軸))"] OP --> Q{"G(S₁) = G(S₂) ?<br/>(幅・正規化位相・tz 名の三つ組=判断 2/3)"} Q -- "はい" --> OK["合成 OK——結果も同じ G 上"] Q -- "いいえ・どちらかが「なし」" --> ERR["静的エラー(黙った空振りの封止)<br/>誘導: snapTo(w)=明示の再整列(判断 5)・rebase=ラベル対応(ADR-40)<br/>同じ所属なら coincides(ADR-38)"] UNION["S₁ | S₂(和)"] --> FREE["検査なし——混合スケジュールの保護(判断 4)"] - 和
\|は整列不問。点は足されるだけで「黙って空振り」の危険が無く、混合スケジュール (「毎営業日と、3/3 の 15:30 の単発」)は発報列の正当な形である。混合の出力は整列「なし」となり、 後段で&/\等に流せばそこで検査に掛かる(安全網は保たれる)。 - 再整列は
snapToで明示する。snapTo(w)は「点→属する w 窓の先頭点」の点変換(ADR-30)で あり、出力は構成的に w の要素グリッドに整列する——既に整列している点には恒等で、整列主張の 付け替えだけが起きる。これが ADR-30 の点変換としての意味に加わる第二の役割(F22 で segmentBy の 所属規約が「snapTo で粒度をそろえる」と定めたのと同じ形が、結合子側の規則になった)。 - tz 名の等値はリテラル文字列の等値(ADR-33 判断 10 の残点の確定): リンク解決の前・正規化
なしで比較する。
"UTC"と"Etc/UTC"、"Asia/Tokyo"と"+09:00"は不一致=安全側エラー。 リンク表(tzdb の Link)も版相対であり、判断の趣旨(版非依存)からはリテラル等値だけが安定する。 同名 tz の版差(premise ごとに tzdb の版が違う場合の市民日境界のずれ)は名前等値の残余リスク として ADR-33 判断 10 が引き受け済み(本検査の射程外——実装系は単一 tzdb 版を既定とする)。 固定オフセット表記の正準形が字句で一意でないと"+09:00"/"+9:00"級の別綴りが安全側エラーを 量産する——F66(b) の確定時に正準形を一意化する(依存を F66 に追記)。 - 検査の対象外: 区間所属(containment)で判定する演算——
within・snapTo・ordinalIn/epochOrdinal・cycle 射影——は、窓が全域を覆う(I5)ため粒度不一致で黙る形が無い。segmentByのマーカーも対象外だが、根拠が異なる——所属は F22 の代表点規約として定義済みで、 マーカーの粒度(瞬間で切るか、snapTo(day)で日境界へ落として切るか)は朔日規約のような意味の 選択であり、どちらでも窓は全域を覆い黙って消える点が無い(stdlib の Kyureki は「朔を含む日から」の 規約をsnapTo(day)で明示する側の実例)。ラベル付きテーブルの束縛名射影(sekki(d))は点の 等値所属だが、外れたら硬エラー(ADR-30)で黙らないため対象外。ただし並行値リストの添字読み (monthNos[epochOrdinal(…)])に同長性検査が無い穴(F62)は本 ADR の射程外として残る—— 「等値所属は黙らない」をこの形へ過大に一般化しない。 - 整列なし同士の
&/\も不許可(判断 3 の帰結の明文化)。瞬間レベルの exact-match 突合を 意図するときの合法形は、細かい原子 grid への明示の再整列——sec = chronos grid 1s級の窓語を premise に立てて両辺をsnapTo(sec)する(分・秒精度のデータには恒等=主張の付け替えだけが起きる。 判断 5)。新機構は要らない。
背景: §1.19 のレビュー(2026-07-07・F56)で「結合子は点集合の演算として定義され、粒度のそろわない
二流の演算(everyDay \ 瞬間列)の規定が無い——点が一致しなければ黙って空振り=サイレント誤結果
(ADR-16 の危険基準)」と指摘された。F22(segmentBy の所属規約)と同形の問題の結合子版であり、
F52 の裁定(細粒度も射程内)と F54(tz 不一致の空振り)がどちらも「点の一致規則」に合流する。
土台は ADR-33 が確定した(点の同一性=chronos 等値・tz の同値=名前等値)。
検査の合格条件を「同一の G」(細分不可)に置く決め手になったのは次の反例である。hour = chronos
grid 1h を持つ premise(Gregorian は hour を持たないのでユーザー定義の原子)で、日次列(整列=
1d)と「毎日 9 時」の列(shift(9, unit: hour) の出力。点は毎日 09:00)の & を考える——書き手の
意図は「月曜の 9 時」のような日単位の突合だが、chronos 等値の交叉は 00:00 と 09:00 が一致せず空に
なる。ここで「両辺の点はどちらも 1h の目盛り上にはある」(日次の点は時の目盛りにも乗る——事実と
しては真)という細分の論法を合格条件に許すと、この空振りが検査を通ってしまう。細分の事実は整列の
推論(判断 2)が使ってよいが、検査の合格条件(判断 3)には使えない——これが「同一の G」の
根拠である。
却下した案:
- 暗黙の整合(粗い側へ自動 snap)。java.time 型の「黙って解決」であり、ADR-16 の危険基準 そのもの。どちらへ揃えるかの選択責任も重い。
- 整列なし同士(瞬間×瞬間)の
&/\を許す案。「両辺とも主張なし=生の chronos 等値の意図が 明示的」という読みは立つが、(a) exact-match は 1ms の差で黙って空振りする脆い形そのもの、 (b) 判断 4 の混合和の出力(なし)が経由地になり、検査の安全網に漏れが開く。合法形は判断 8 の 明示再整列で確保できるため、危険側を既定で開ける理由がない。 - 細分・包含による自動合格。上記の反例(
1d×1hの空振り)が通る。 - 和
\|にも同一整列を要求(全結合子で一律)。教えやすさは増すが、混合スケジュールという 正当な形が表現不能になる(整列を「外す」キャストは存在せず、新語の導入を強いる)。和には 黙る危険が無いので、統治の根拠(ADR-16)自体が立たない。 - 実行時検査。整列は導出構造(生成子・字句・段の伝播)から静的に決まり、データの値に依存 しない。静的に取れる誤りを評価まで遅らせる理由がない(tz 名の妥当性は版相対=データ相対 エラーのままで、これは ADR-33 判断 9 の層。整列の一致判定は名前と構造だけで静的に閉じる)。
- 粒度をストリーム型のパラメータにする(型の分裂)。整列は型ではなく式の静的性質(検査で読む 註釈)として置く。型は一本(ADR-05)・点はデータを運ばない(ADR-30・ストリームの要素は TZ を 運ばない ADR-33 判断 2)の体系を崩さない。F52 の裁定(型は粒度を固定しない)とも整合する。
帰結:
- spec §4.5(結合子)に整列の定義・伝播表・検査規則を追記し、§4.4(
snapTo)に第二の役割 (判断 5)、§4.6(filter(on:))・roll/shiftの記述に軸所属の検査を追記する。EBNF は変更なし。 - glossary に「整列(alignment)」を追加。
- ADR-33 判断 10 の「等値をリンク解決の前後どちらで取るか」は判断 6 で確定(改訂節は不要—— ADR-33 は本 ADR への送りを明記済み)。
- カレンダー実体(ADR-35)の標準導出
everyDay \ C.nonWorkingは本検査の適用先の代表例—— 実体のtz:(内側固定)と利用側の tz が食い違えば静的エラー。snapTo(day)の明示整合は 「chronos 上の重なりを利用側の日界で読む」意味であり、「同じ日付ラベル」の意味ではない (東京の日先頭は NY の前日に floor される)。クロス tz で「同じ日付」を意図する合成(TSE と NYSE の 共通営業日)は chronos 等値では原理的に書けず、日付ラベル保存の再錨が要る——新綻び F69 (宿題・追加拡張)として記録。 - 時刻付き・混合スケジュールに例外日を適用する形(「毎営業日 9 時の通知から祝日を除く」)は、
等値の差
\ではなく窓所属ベースの結合が要る——従来は黙って空振りしていた形が本検査で 顕在化した。新綻び F68(宿題・追加拡張)として記録(snapToは時刻を潰すので修理形にならない。 日単位のスケジュールへのschedule \ blackoutDaysは従来どおり合法)。 - 外部供給宣言 socket(未確定)への要件が一つ増える: 器の宣言はデータの整列の主張を含む必要が ある(字句が無いため判断 2 のテーブル規則が適用できない——socket 宿題に追記)。
- ADR-05(型は一本)に改訂ポインタを追記——整列は型の分裂ではなく検査用の静的性質、の位置づけ。
- リファレンス実装: ストリーム値に整列タグを載せ、判断 2 の伝播と判断 3 の検査を実装する
(プロトタイプは評価時検査=静的検査の近似。固定オフセット TZ のみのため tz 名等値の検証は
名前比較の器だけ実装し、既知の制約に明記。
strideの軸検査は軸を評価しない現行構造のため 未実装=制約に明記)。 strideの軸相対カウントの操作的意味の二義(軸の点列上を n 歩か・入力の点を n 個ごとか)は 新綻び F70(宿題・明文化)として記録——判断 3 の stride 検査はこの明文化を前提にする。- 残る宿題への影響: F61(範囲外の点の出自)・F62(並行リスト同長性)は独立のまま(判断 7 の とおり F62 の穴は本 ADR では閉じない)。F64(同名適用の二義)とも直交。DST 隙間・重複の解決規約 (ADR-33 の将来 opt-in)が入っても、整列の計算は「最初の瞬間」規則(ADR-33 判断 4)の上で不変。
改訂(2026-07-08・ADR-38): 二点を改める。(1) 判断 3 の検査リストから stride(n) を削除——
stride は入力相対に確定し(F70・ADR-38 判断 10)軸を読まないため突合が無い(軸との整列は前段の
filter(on:) が検査する)。帰結の「stride 検査は未実装=制約に明記」も解消(検査自体が不要になった)。
(2) 判断 7 の免除系(区間所属)に窓所属述語 coincides(S, w, d)(仮称・ADR-38)を追加——
等値でなく所属で結合する明示手段であり、F68 の解。ただし免除は無条件ではない: S の整列が市民時
グリッドで w の要素グリッドと tz 名不一致なら静的エラー(ADR-38 判断 5——本 ADR 判断 6 が等値側で
止めていたクロス tz の黙った 1 日ずれを、免除系の裏口から再開させない)。coincides は chronos 所属で
あり「同じ日付ラベル」の所属ではない(そちらは F69 の再錨の領分のまま)。あわせて判断 3 の
エラーメッセージに分岐案内を足す——「同じ点が欲しいなら snapTo で整合・同じ所属(日)が
欲しいなら coincides」(等値差を coincides に書き換える逆方向の迂回は悪化——ADR-38 判断 8)。
改訂 2(2026-07-08・ADR-40): 判断 7 の免除系にも tz 名の検査を敷く——rebase(ADR-40)が
クロス tz 整列のストリームを常態化させると、免除系(within・ordinalIn・選択子・cycle/値射影)は
tz を見ないため「ラベル 1 日ずれの束ね・曜日読み」が黙って通る(東京 3/1 の日先頭を NY premise の
within(month) に流すと 2 月窓へ)。市民グリッド入力 × 窓要素グリッドの tz 名不一致は静的エラー
(幅・位相は不問のまま=区間所属の免除は維持・tz だけは日付座標系そのもの——ADR-38 判断 5 の
coincides 検査の一般化。既存の潜在穴でもある)。snapTo は除外(chronos 所属が文書化済みの意味)。
改訂 3(2026-07-13・ADR-45): 整列の値に第三状態「空虚適合」を追加——空テーブル
([] covering: …・ADR-45)は点を持たず、どの整列にも違反し得ないため、判断 2 の「G か『なし』の
二値」を三値に拡張する。空虚適合は判断 3 の検査に通り(相手が「なし」でも通す——「なし」=
主張できない・検査に落ちる、との対比)、判断 4 の和および &/\ の出力整列は相手側を継承、
保存系の段(filter・選択子・shift 窓語・roll・snapTo・rebase)は空虚適合を保存する。判断 7 の
免除系 tz 名検査(改訂 2)は市民グリッド同士の規定のため空虚適合には掛からない。詳細は ADR-45。