日本語 · English

segmentBy — interval-sequence windows (cut at markers)

Translated from the canonical Japanese page reference/segmentBy.md. The source_sha above records the source revision; a consistency check flags this page when the Japanese original changes.

Category: window (body-layer core) / Signature: segmentBy(m, edges:, empties:) : Stream -> Stream(interval) / name settled (spec §5.4)

The four ways to make windows, drawn over the same nine-week strip of days (Mon 2026-01-05 to Sun 03-08): within(month) divides the axis by month, split divides the parent week into dependent windows of widths [5, 2], span bundles days five at a time (from the epoch, so starting 1/2), and segmentBy cuts intervals at new-moon markers with no window before the first marker (edges: drop). The dots in each row are the first of each window.

Meaning

Takes an arbitrary stream m as markers and makes each half-open interval [mᵢ, mᵢ₊₁) between adjacent markers a window. Every window whose boundaries are given by a sequence of points — fiscal closes, lunar phases (a lunisolar calendar cut at new moons), weeks (cut at the wkst-labeled day) — stands on this.

Half-open intervals. Top: on a number line from 8:00 to 16:00, the session [9:00, 15:00) includes 9:00 (filled dot) and excludes 15:00 (hollow dot); hourly |> filter(t => isOpen(t)) on 2026-01-05 gives the six points 09:00 to 14:00, and 15:00 does not appear. Bottom: the closed ranges 9:00–11:59, 12:00–17:59 and 18:00–23:59 leave a 59-second gap at each seam, whereas the half-open windows [9:00, 12:00), [12:00, 18:00) and [18:00, 24:00) meet at the same point with no overlap and no leak: segmentBy over the markers 9:00, 12:00, 18:00 and 24:00 (2026-01-06T00:00) gives the first points 09:00, 12:00, 18:00 and the last points 11:00, 17:00, 23:00.

Unlike the partition type, exhaustiveness is not guaranteed, so the two arguments that state the meaning of the gaps explicitly are mandatory declarations (I5; omission is a static error). This closes off, by syntax, the accident in which firings silently vanish on missing data (ADR-15’s “accidental empty”).

Arguments

Argument Value Meaning
m stream expression the markers (the intervals’ boundary points)
edges: drop / clip / error treatment of points before the first marker / after the last marker (discard / make a partial window / error)
empties: keep / drop / error treatment of zero-element windows between markers (keep as a legitimate empty / discard / error)
labels: list (literal / list binding name) or the cyclic form cycle list anchor: real-day (ADR-47, below) a parallel label list for the window sequence (ADR-39); reading is binding-name projection name(d). For the combination constraints see “Preconditions and tightening rules” below — it cannot combine with edges: clip, empties: drop, or label:
label: lambda (p => expr) a computed label per window (ADR-34; for index expressions and conditional computations that do not fit labels:)

Examples

Lunisolar months cut at new moons — take the first day (the new-moon day) of each month. Lunar New Year 2026-02-17 appears:

# eval: 2026-01-01..2026-05-01
@JP
newMoons = [2026-01-19T04:52, 2026-02-17T21:01, 2026-03-19T10:23, 2026-04-17T20:52]
lunarStart = newMoons |> snapTo(day)
everyDay |> segmentBy(lunarStart, edges: drop, empties: drop) |> first
#=> 2026-01-19 2026-02-17 2026-03-19
#~> 範囲外 2026-01-01..2026-01-19(newMoons covering 2026-01-19T04:52..2026-04-17T20:52)
#~> 範囲外 2026-04-17..2026-05-01(newMoons covering 2026-01-19T04:52..2026-04-17T20:52)

With edges: drop, nothing past the last marker 4/17 becomes a window (no silent continuation at the data’s edge).

labels: — a parallel label list for the window sequence (ADR-39)

Binds a data label sequence to the window sequence. Reading is binding-name projection — name(d) returns the label of the window that point d belongs to (defining equation name(d) ≡ labels[window-sequence ordinal]; in the window-sequence ordinal, the window starting at the first marker within the effective coverage is 0). A same-length check runs at evaluation: list length == window count (the window count is coverage-based = the marker count, independent of the evaluation range and the materialization range) — forgetting to update markers and labels as a pair breaks loudly as a static error (the vessel for F62):

# eval: 2026-01-01..2026-07-01
@JP
newMoons = [2026-01-19T04:52, 2026-02-17T21:01, 2026-03-19T10:23, 2026-04-17T20:52]
  covering: 2026-01-19..2026-04-30
lunarMonth = everyDay |> segmentBy((newMoons |> snapTo(day)), edges: drop, empties: error,
                                   labels: [12, 1, 2, 3])
lunarMonth |> first |> filter(d => lunarMonth(d) == 1)
#=> 2026-02-17
#~> 範囲外 2026-01-01..2026-01-19(newMoons covering 2026-01-19..2026-04-30)
#~> 範囲外 2026-05-01..2026-07-01(newMoons covering 2026-01-19..2026-04-30)

Preconditions and tightening rules (all static errors; ADR-39 decision 4): it cannot combine with edges: clip (pseudo-windows shift the ordinals); it cannot combine with empties: drop (removing empty windows compacts the ordinals); it cannot coexist with a label: lambda (doubling the label source); it does not attach to rule markers (infinite sequences of the week class — periodic labels go to cycle, computed numbers to ordinalIn or label:); composite markers (t1 | t2) split the effective coverage so the window count is not determined — fix it first with a binding-postfix coverage claim (covering:). The reading port for an empty window’s label (empties: keep) is interval membership — any point within the window interval, in practice the marker point itself (the form in which an absent period’s number still stands). For several parallel sequences (numbers and names) the canonical form is separate window bindings over the same markers (the check bites twice; ../../stdlib/kyureki.md (Japanese) §1).

labels: cycle — a periodic label for the window sequence (ADR-47)

The shape “window sequence = the calendar’s unit, label = a fixed period” (the twelve branches of sekki-cut months, the monthly nine stars, the moon’s twelve signs) is written with the cyclic form labels: cycle list anchor: real-day. The semantics are identical to cycle on a window binding — “the window containing the anchor carries the first label” (list[(window-sequence ordinal − anchor window's ordinal) mod N]; negatives normalized by the modulus). No same-length check is imposed — as the covering grows over multiple years and markers increase, the anchor stays and the expression does not change by a single character (compatibility with external supply 〈ADR-46〉 is the aim of this form).

# eval: 2026-02-03..2026-02-06
@JP
setsu = [2026-01-05, 2026-02-04, 2026-03-05, 2026-04-05] covering: 2026-01-05..2026-04-05
sekkiMonth = everyDay |> segmentBy(setsu, edges: drop, empties: error,
                                   labels: cycle [寅, 卯, 辰, 巳, 午, 未, 申, 酉, 戌, 亥, 子, 丑]
                                   anchor: 2026-02-04)
everyDay |> filter(d => sekkiMonth(d) == 寅)
#=> 2026-02-04 2026-02-05

label: (ADR-34)

The parenthesized named-arg segmentBy(m, edges:, empties:, label: (p => expr)) attaches a computed label to each window (p = the window’s first point; lazy evaluation at projection time). When merely pasting a data column, labels: is canonical (ADR-39, ADR-30 revised); label: is for computations involving index expressions and conditions (../../stdlib/kyureki.md (Japanese) §7 (5)).

Externally supplied data labels — the form whose expression stays unchanged as coverage grows (reflux mail 20 §2, 2026-09-03): receive the markers through external and attach its label column (supplied together with the dates under the same-length contract of ADR-30) with label: (p => markerTable(p)). p is the window’s first point — the marker point itself — so the projection returns that marker’s label as is. Even for non-periodic data labels such as lunisolar month numbers (a leap month repeats the preceding number), the same-length check lives on the supply contract’s side (契約違反: labels は時点列と同長), and the text of the expression does not change with the yearly coverage update — “if the date is authoritative, so is the label that names it”, realized with the existing machinery:

# eval: 2026-02-15..2026-02-22
# resolve: saku = dates 2026-01-19 2026-02-17 2026-03-19 2026-04-17 2026-05-17 covering: 2026-01-01..2026-05-31 asof: 2026-09-03 labels: m12 m1 m2 m3 m4
premise L { calendar-system: Gregorian; tz: "Asia/Tokyo"; wkst: Mon; source: "moon/saku"
  saku  = external(kind: dates, labels: [m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12])
  lunar = day |> segmentBy(saku, edges: drop, empties: keep, label: (p => saku(p)))
}
@L
everyDay |> filter(d => lunar(d) == m1)
#=> 2026-02-17 2026-02-18 2026-02-19 2026-02-20 2026-02-21

When the supply grows by one new moon (2026-05-17 with m4), lunar simply gains one window and the definition above is untouched (measured: with five new moons, m1 = 30 days, m2 = 29 days, m3 = 4/17–5/16). The value-domain declaration labels: [m1, …, m12] is static knowledge (a leap month repeats a name inside the domain). A point sequence derived by filter carries no labels (labels are properties of tables and windows — ADR-30/34/42), so the projection does not stand when the new moons are filtered out of another table — supply the new moons as their own external (if you already compute them upstream, just ship the dates and the month numbers together).

Using labels as numbers (reflux regular mail 2026-09-08 §3): the value domain of labels: is enumeration names (labels: on external accepts identifiers only — ADR-30’s value-domain declaration is static knowledge), so a definition that does arithmetic on a label, such as a lunisolar month number, keeps a name-to-number function inside the premise: monthLabelNo = l => l == m1 ? 1 : l == m2 ? 2 : … : 12 (a nested conditional; the readability cost is explicit). In the supplier’s measurement the rokuyō (大安) definition written this way matched the static labels: version point for point. A one-word projection that takes the ordinal from the declaration order (something like ord(lunar(d))) is not in the 1.0 vocabulary; it is a post-1.0 candidate in 90-open-questions.

⚠ Give this function a name that does not collide with a standard word — monthNo is a public word of the standard Gregorian premise, used both by the label projection of month (stdlib/gregorian.md §1) and as the calendar-coordinate value function (§2). Redefining it inside a premise collapses every month-number read into the default branch of the conditional (== 12 matches every day of the window; anything else matches nothing; no warning). In the Gregorian with { … } form month(d) is affected too; a direct monthNo(d) call collapses even in a flat premise (measured in the reflux urgent mail of 2026-09-23 — the supplier renamed their function and this page renamed its example).

Pitfalls

within · snapTo (granularity-matching the markers) · cycle (periodic labels) · table literals (bringing marker data in) · ADR-07/08/15 · I5.

A segmentBy window sequence borne by rule markers (no coverage annotations) is accepted as an effective partition for within’s w and for split’s parent and by: (ADR-48; the term is in spec §6.3) — regular period cuts like the 4-4-5 fiscal calendar’s “periods” can be written in either the segmentBy canonical form or the split form (worked example: the recipe “4-4-5 fiscal calendar”; the extensional match on the split side is the weekPart doctest on the split page).