「寄せる」と「数える」は別の操作である——roll と shift 入門

Kairos

「25 日が日曜なら前の金曜に」

給与振込の要件は、たいていこう書いてある。支給日は毎月 25 日。土日祝にあたる場合は直前の営業日。前半は cron でも書ける。後半が、この連載で何度も出てきた「書けない」の壁である。

Kairos ではこう書く:

everyDay |> within(month) |> nth(25) |> roll(Preceding, on: bizDay)

2026 年の秋で実測すると(祝日はシルバーウィーク 9/21〜23・スポーツの日 10/12・文化の日 11/3・勤労感謝の日 11/23 を宣言済み):

2026-09-25   # 金曜。そのまま
2026-10-23   # 10/25 は日曜 → 直前の金曜 10/23 へ
2026-11-25   # 水曜。そのまま
2026-12-25   # 金曜。そのまま

この roll が今日の主役の片方である。もう片方は shift。この二つ、どちらも「日付を動かす」ので混同されやすいが、やっている操作が根本的に違う。今日はその区別だけを、ゆっくり書く。

roll は「寄せる」——動かないことが第一希望

roll(Preceding, on: bizDay) の意味は「その点が bizDay 軸の上に無いときだけ、Preceding (直前)の方向へ寄せる」である。大事なのは条件付きであること——

  • 25 日が営業日なら、動かない。9 月・11 月・12 月がそうだった
  • 25 日が営業日でないときだけ、指定の方向へ避難する。10 月がそうだった

つまり roll は避難規則である。第一希望はあくまで 25 日で、居られないときにどちらへ逃げるかだけを決めている。方向は 2 つ:

  • Preceding——直前へ。給料日の定石(前倒し=労働者有利)
  • Following——直後へ。引き落とし・支払期限の定石(後ろ倒し=債務者有利)

同じ 10/25(日)が、Preceding なら 10/23(金)へ、Following なら 10/26(月)へ動く。どちらが正しいかは業務が決めることで、言語の仕事はどちらのつもりかを書かせることである(前回の言い方をすれば、ここを既定で片方に倒すのは「黙りの既定」になる)。

shift は「数える」——動くことが目的

一方 shift は、動くこと自体が目的の操作である。「月末の 3 営業日前」を書く:

monthEnd |> roll(Preceding, on: bizDay) |> shift(-3, unit: bizDay)
2026-09-25   # 9/30(水)から営業日を 3 つ遡る: 9/29・9/28・9/25(21〜23 は祝日)
2026-10-27   # 10/30(金)から: 10/29・10/28・10/27
2026-11-25   # 11/30(月)から: 11/27・11/26・11/25
2026-12-28   # 12/31(木)から: 12/30・12/29・12/28

shift(-3, unit: bizDay) は「bizDay の目盛りで 3 つ戻る」。土日祝は目盛りに入っていないから、数えるだけで自動的に飛ばされる。9 月末の「3 営業日前」が 9/25 まで遡るのは、シルバーウィークが目盛りから抜けているからである。

まとめると——roll は条件付きの避難(動かないのが基本)、shift は無条件の移動(動くのが目的)。「祝日なら前営業日」は roll、「N 営業日前」は shift。要件の日本語にどちらの言葉が出てくるかで、使う語もそのまま決まる。

間違えたら、言語が教えてくれる

では 25 日から直接 shift で営業日を数えようとしたら? やってみると:

everyDay |> within(month) |> nth(25) |> shift(-1, unit: bizDay)
#=> shift: 点が軸上にない(先に roll で有効点へ寄せる)

静的エラーで止まる。10/25(日)は bizDay の目盛りに載っていないので、そこから「1 営業日」を数えることは定義できない——土曜から数え始めるのか日曜からか、解釈が割れる。割れる解釈を黙ってどちらかに倒さず、「まず roll で目盛りに載せてから数えよ」と誘導まで付けて止まる。前回書いた「多義は受理しない」が、いちばん身近に現れる場面である。

だから定石は「寄せてから、数える」——さっきの月末 3 営業日前の式がroll |> shift の順で並んでいたのは、この定石そのものである。

▶ Playground で 3 式を実行する

まとめ

  • roll = 寄せる。その日に居られないときだけ動く避難規則。方向(Preceding/Following)は 業務が決める——だから書かせる
  • shift = 数える。軸の目盛りで N 個動く移動。土日祝は目盛りに無いから自動で飛ぶ
  • 目盛りに載っていない点から shift は始められない——寄せてから数えるが定石で、間違えると 誘導つきの静的エラーが教えてくれる

次回も引き続き、言語の節目が近づく中で足元を固める話を書く予定である。


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

コメント

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