日本語 · English

Kairos Language Specification — 3. The Premise Layer

Translated from the canonical Japanese chapter spec/20-premise-layer.md. The source_sha above records the source revision; a consistency check flags this page when the Japanese original changes.

The premise layer assembles and supplies the premises on which the body layer stands — calendar systems, calendars, axes, roll conventions, and the like.

3.1 What a premise is

A premise is the collective term for the preconditions under which the interpretation of an expression, clause, or phrase holds. It includes: calendar system (calendar-system), axis, calendar, roll convention, granularity, TZ, asof, WKST, and provenance (source). It plays for expressions the same role that a contract’s preamble (recitals) plays for the clauses that follow — laying down in advance the premises on which they stand. Streams (values) and operators (verbs) are evaluated on top of premises, but are not premises.

3.2 The preamble (premise declarations)

A preamble is placed before body expressions and governs the expressions that follow (effective until the next preamble). One and the same binding operation comes in three forms, long and short.

Definition (multi-line allowed) — defines the contents that @name bundles.

premise JP {
  calendar-system: Gregorian      # calendar system (structure)
  calendar:        TSE            # calendar (policy: business days)
  tz:              "Asia/Tokyo"   # TZ and source are string literals (ADR-32)
  wkst:            Mon
  asof:            latest
  source:          "cao.go.jp/official"
}

Lightweight form — lays a predefined bundle down as the preamble. @name is the same reference whether it names a single-value alias (@TSE) or a multi-member bundle (@JP).

@JP
monthEnd |> roll(Preceding, on: bizDay) |> shift(-3, unit: bizDay)

Full form, inline — makes a preamble on the spot, without naming it.

premise { calendar-system: Gregorian; calendar: TSE; tz: "Asia/Tokyo"; wkst: Mon }

To bracket the scope explicitly, use the block form @name { … } (its interior is a sequence of bindings and body expressions). Stage arguments override the preamble innermost-first.

3.3 Member slots and the governance of omission

The members are calendar system, calendar, axis, roll, granularity, TZ, asof, WKST, and provenance. Dangerous members — those whose mix-up produces silent wrong results — are declaration-required leaning; safe members may default. A dangerous member that can be resolved neither in the preamble nor at a stage is a static error.

Member When omitted Why it is dangerous
calendar-system (calendar system) May default (Gregorian) A mix-up is caught by name ambiguity
calendar (business-day calendar) Declaration required No universal default exists / silently yields a different day. The value is a calendar entity (identity check, §3.9)
axis (axis) Declaration required (no language default) An axis mix-up gives a different result (shift’s 3 days vs. 3 business days)
roll (roll convention) Declaration required Mixing up how invalid points are moved is a silent wrong result
wkst Declaration-required leaning The origin of “the Nth” flips
tz Declaration-required leaning The local day drifts
source (provenance) Declaration-required leaning Confusing the official edition with a local override
granularity (granularity) May default (“day”) Explicit or default, the result is often unchanged
asof May default (evaluation time) Declare it when reproducibility matters

Folding axes and rolls (scope defaults)

on: (roll, filter) and unit: (shift) name, premise-relative, the axis being operated on (bizDay / day / hour …; a cycle name (weekday) is a label and does not resolve to a stream, so it cannot stand as an axis; stride is input-relative = takes no axis — ADR-38). The axis resolves against the in-scope calendar: (bizDay under @JP is TSE’s business days). Because axis names resolve, in the in-scope premise, to a stream of valid points, a derived stream may be passed directly as an axis (e.g. roll(Following, on: nonHoliday) for substitute holidays. ADR-26). When a premise name stands in the axis position (on: TSE), the identity check requires it to be a calendar entity, and it is reinterpreted as “override calendar: for that stage only and read the standard derivation bizDay” (§3.9, ADR-35). The redundancy of several stages writing the same axis can be folded by declaring the axis once as the preamble member axis:; a stage that omits it resolves from the in-scope axis:. This is a lexical scope default (default → evaluation context → block declaration → stage argument, innermost wins), not back-stage inference, so each stage’s locality is preserved. The same mechanism lets a roll convention be declared once as the preamble member roll: (in a bundle definition, or postfixed to the lightweight form) and inherited by stages. Being a dangerous member, however, the convention is recommended to stay explicit.

# explicit (write the axis at each stage)
@JP
monthEnd |> roll(Preceding, on: bizDay) |> shift(-3, unit: bizDay)

# folded (give the body the axis default as a postfix)
@JP axis: bizDay
monthEnd |> roll(Preceding) |> shift(-3)

# a different axis for one stage only (innermost wins)
@JP axis: bizDay
monthEnd |> roll(Preceding) |> shift(-3, unit: day)

3.4 Name resolution is premise-relative

Names resolve to values under the current premise. If several premises supply the same name, it is ambiguous and requires qualification (Gregorian.month). “Meaning is premise-relative” extends beyond values to names. Unique: bare name; ambiguous: qualify with the enclosing entity; unresolved: error.

3.5 Value expressions and variables

The premise layer has value expressions that are not time streams. The operators are chosen so as not to collide with the combinators (&, |, \).

Kind Symbols
Arithmetic + - * /, remainder mod, integer division div (words). div is floor division and mod the mathematical remainder (they disagree with trunc for negative dividends. ADR-31 revised, F63)
Comparison < <= > >= == !=
Logic and, or, not (words; the symbols &/\| are reserved for the combinators)
Conditional ternary cond ? a : b
List literal [a, b, …], indexing l[i] (0-based), membership predicate x in l
String literal "…" (no newlines, no escapes; used for TZ and source values. ADR-32)
Binding / equality = is binding (definition), == is equality comparison

Bindings of value functions and value constants may be written both inside premise blocks and at file top level. Top-level bindings are lazily resolved in the in-scope premise, just like sugar definitions (§4.8) — “= is the same binding everywhere” (ADR-28).

Lambdas and higher-order functions — some operators take not a value but a function as an argument (higher-order functions). The window-generating words span/split (§3.6) and the filter filter (§4.6) are all higher-order functions that receive “how to treat each element” as a function (cycle takes a list of labels, so it does not count). An anonymous function passed on the spot is called a lambda, written arg => expr (the arrow => is distinct from the type notation ->). For example, y => y mod 4 == 0 is the function “take a calendar year y and return whether it is a multiple of 4.” A lambda may be bound to a name (isLeap = y => …) or passed directly to a higher-order function in place (filter(x => …)).

Predicates and variables — a predicate (a lambda returning a boolean) binds each element of its subject. where is merged into filter: filter takes both premise predicates (on:) and value-expression predicates (lambdas). References go through the binding name, so nesting is never ambiguous.

isLeap = y => y mod 4 == 0 and not(y mod 100 == 0 and y mod 400 != 0)

3.6 Primitive definitions of calendar systems

A primitive definition (Gregorian and the like — a root with no derivation source) builds a calendar system by carving the continuous base Chronos into windows, and produces public words. The window-generating words are three plus one (grid/span/split, which make windows, and cycle, which produces labels, not windows). All three window-making words produce partition-type windows (§4.2), so exhaustiveness and non-overlap are structurally guaranteed (I5). This section explains how to write a primitive definition, with Gregorian as the example. For the exhaustive account of Gregorian itself (each word, the separation of weekday and WKST, scope), see stdlib/gregorian.md.

Dependencies are primarily bottom-up aggregation: the atom day is bundled into month, and that month into year (month is the basic grouping). Only year’s dependent window (quarter) is made by split-ting year, so that it tracks changes to year automatically.

Word Kind Meaning
grid(w) Uniform partition Tiles the continuous axis into equal widths w. Makes the calendar’s atoms. w follows the civil-time width convention (1d = 1 civil day, not a fixed 86400s; a civil day is 23–25 hours on DST transition days. Leap seconds are out of scope = chronos is a uniform idealized axis without leap seconds (each UTC day = 86,400 seconds). ADR-11/12/33). Phase defaults: civil-time widths align to the start instant of each civil day in the in-scope tz: (midnight on ordinary days. ADR-31 revised); elapsed-time widths align to the epoch; overridable with anchor: (ADR-31)
span(f) Variable aggregation (bottom-up) Bundles a sequence of finer units into contiguous windows. f = n => count binds the ordinal n (epoch-origin) of the window being generated and returns the number of units to bundle. The count may be variable (month’s day counts) or constant (year’s 12)
split(g) by: u Variable division (top-down) Divides a parent window into contiguous subwindows. g = y => [widths…] binds the parent’s ordinal y and returns the list of subwindow widths. by: u makes the width unit explicit. That the widths sum to the parent is checkable under I5. Used for dependent windows
cycle(labels) anchor: Parallel labels Attaches repeating labels to the target partition windows. Produces labels, not windows. The period length is arbitrary (7, 10, 12, 60, …) and so is the target window (not just day’s weekdays — month and year work too; the sexagenary cycle is year cycle […]). anchor: is an instant meaning “the target window it belongs to takes the first label”. The binding name reads, in value expressions, as a point → label value function (filter(d => weekday(d) == Mon); resolution is two steps: point → containing window → label. ADR-27)

Leap is a value, not a window (the pivot that fixes the dependency direction)

“Does February have 28 or 29 days?” looks like a dependency on the window year, but it is in fact a value dependency computable from the month’s serial number m (derive the calendar year and month position from m, then decide with isLeap). Hence month need not depend on the year window, and month can stand as the basic grouping (bundling day). If month were made a child of year (year split), then when a derivation (the fiscal calendar, §3.7) re-bundles year from month, month ↔ year would become circular. Viewing leap as a value dependency and placing month as the parent keeps the cycle from ever arising — this is the ground on which the fiscal calendar can be written in one line, with no workaround.

Public words

Only bindings at the block’s top level are public, referenced with . as in Gregorian.month. Boundaries are derived by reusing the body layer’s selectors (zero new machinery). The generator monthEnd (yielding the calendar-day month end) is in reality this public boundary word month |> last itself: a “generator” is a public boundary word of a primitive definition (not a separate mechanism).

premise Gregorian {
  day     = chronos grid 1d                                 # atom (carve the continuous axis into civil days)
  weekday = day cycle [Mon, Tue, Wed, Thu, Fri, Sat, Sun] anchor: 2000-01-03

  isLeap      = y => y mod 4 == 0 and not(y mod 100 == 0 and y mod 400 != 0)
  daysInMonth = m => monthLengths(isLeap(yearOf(m)))[monthOf(m)]  # leap computed as a value from m (no year window)
  month   = day   span daysInMonth label: (p => monthNo(p))        # basic grouping: bundles day (year-independent)
  year    = month span (_ => 12) phase: 0 label: (p => yearNo(p))  # bundles month twelve at a time
  quarter = year  split (_ => [3, 3, 3, 3]) by: month       # year's dependent window (auto-tracks in fiscal calendars)

  weekStart = day |> filter(d => weekday(d) == wkst)        # wkst lazily resolves the user-side preamble declaration
  week      = day |> segmentBy(weekStart, edges: clip, empties: error)

  monthStart = month |> first                               # public boundary words (selector reuse)
  monthEnd   = month |> last
  yearStart  = year  |> first
}

The auxiliary value functions yearOf(m) / monthOf(m) / monthLengths(bool) are value expressions that return the calendar year, month position, and day-count list from a month ordinal m (they do not reference the year window). Bindings resolve in dependency order (day → month → year → quarter). Mutual reference is allowed; cycles are errors.

The epoch at which the window ordinals n and m originate is by language default 1970-01-01T00:00 (in-scope tz). A calendar system with a different basis (a Martian calendar, etc.) may override it with the primitive-definition member epoch: (a value intrinsic to the calendar system; it cannot be placed in the user-side preamble. Ordinals are 0-based — the coordinate in which monthOf(m) = m mod 12 holds. ADR-31). Since the epoch is a preimage under the in-scope tz’s mapping, a different tz means a different point on chronosepochOrdinal and span ordinals are premise-relative coordinates (ADR-33 decision 7).

week references wkst: (a preamble member) on its right-hand side and is lazily resolved in the user side’s in-scope premise (the same rule as sugar definitions, §4.8; within(week) with no wkst: declared is a static error). Generation uses segmentBy (interval-sequence type), but because the weekday cycle lets exhaustiveness and non-overlap be proven by the I5 check, it can be used with within(week) — partition-hood is established by the check, not by the generating word (stdlib/gregorian.md §4.5).

The right-hand side of a premise binding may contain, besides value expressions and window-generating words, body-layer stream expressions (pipe sequences like weekStart above, segmentBy, table literals §3.8) — a consequence of “vocabulary is shared with the body layer” (ADR-25); the type of a public word is determined by the type of its right-hand side (window / label / value / stream. ADR-26).

Gregorian is a mathematical model on an idealized continuous time axis; it bears no historical facts (the date deletion/duplication of the 1582 calendar reform, regional differences, leap seconds (positive and negative; chronos idealization = ADR-33), the calendar’s period of validity). When needed, handle them with a separate premise (a historically attested edition) or with asof/source annotations.

The firing point of tz: (ADR-33): tz: is declaration-required leaning and fires at use (isomorphic to wkst:) — an evaluation that requires the chronos→civil-coordinate mapping — the anchor of a date literal, civil-width grid, snapTo(day), and the like — errors if it cannot resolve tz: in scope. Expressions that do not use the mapping stand without a declaration. The definition of TZ (mapping, civil day, gaps/overlaps, leap seconds, versions) is in ADR-33.

3.7 Derived definitions of calendar systems

A derived definition makes a new premise by overriding or extending an existing premise’s public words (the premise → premise closure). The core is with override — on top of the base, only the named public words are replaced; the rest is inherited.

premise Fiscal = Gregorian with {
  year = month span (_ => 12) phase: 3 label: (p => yearNo(p))   # April start. label: is not inherited on override = attach it alongside (F96)
  # month is untouched, so calendar months and month ends stay fixed (inherited from Gregorian as-is)
  # quarter's inherited definition (year split by month) auto-tracks the new year → fiscal quarters Apr-Jun/…
}

Name resolution (mechanism A):

Dates do not move (base-fixedness I1) — a derivation moves only the windows’ cut points. Calendar days, day/month, monthStart are fixed. 2026-03-01 remains “March 1”; only the year window it belongs to changes (fiscal 2025 = Apr2025–Mar2026).

The pipe is sugar; expansion is a single phase shift of span — the everyday form is premise Fiscal = Gregorian |> rephase(+3, on: year, unit: month). rephase is sugar expanding to the with above, with the expansion rule:

rephase(δ, on: W, unit: U)  ≡  W = U span (_ => k) phase: ((φ₀ + δ) mod k)   # negative δ is also normalized by the modulus (F65)
#   k  = the number of U's contained in W (12 for year ⊃ month)
#   φ₀ = W's phase in the base (0 for Gregorian's year)

View W as “a span bundling U in groups of k” and simply advance its phase by δ. Pairs where k is not constant (shifting month ⊃ day in units of day, etc.) are not fiscal-calendar-type operations and lie outside rephase’s reach (a separate operator if ever needed).

An orthogonal, separate knob — year numbering (“fiscal 2025” = the starting calendar year / US FY = the ending calendar year) is a projection of ordinals and labels independent of window cutting, with conventions that differ by country. It is not baked into rephase but absorbed into the labeling side (label: attachment expressions) of the window→value projection family (§4.9, ADR-27). The binding rule of attachment expressions is settled by ADR-34 — the lambda receives the window’s first point (label: (p => yearNo(p)) labels with the starting calendar year); adjacent-window reference is out of reach (§4.9). rephase expands preserving the base’s label: (the cut knob and the label knob are orthogonal), and the composite phase is normalized modulo k (F65).

3.8 The intake for data (table literals)

Sequences that cannot be generated from periodic rules — the gazette-proclaimed vernal and autumnal equinoxes, new moons, the twenty-four solar terms, year-limited special days — are brought into a premise with a table literal (ADR-26). A list of instant literals is promoted to a time-stream constant (the syntax is identical to value lists, §3.5; the type is determined by the elements). The sequence must be ascending (out of order or duplicates are static errors). The empty list [] is promoted only when covering: is postfixed (the empty table = the primary form of “zero points, but the coverage is to be claimed”. ADR-45 — the vessel for writing the supply layer’s “nothing at all yet” as a legal source. Having no elements, its type is determined by the presence of covering:, and each of the containment, ascending-order, and labels: same-length checks (only labels: [] is legal) holds vacuously; alignment is vacuous conformance = §4.5).

premise JPGazette {
  source: "cao.go.jp/official"      # provenance (declaration-required leaning: mixing up data provenance is dangerous. string = ADR-32)
  asof:   2025-02-03                # edition (data as of this gazette)
  tz:     "Asia/Tokyo"              # fixes the data's civil days on the inside (ADR-33 decision 10, F54)
  vernalEquinoxDay = [2025-03-20, 2026-03-20, 2027-03-21] covering: 2025..2027
}

The naming status is in §5.4: grid, span, split, cycle, anchor:, phase:, by:, with, chronos (the lexical name of the base. ADR-29), and axis: (the operating axis) are settled, and RC2 also settled covering:, labels:, and label: (table and attachment sides). Naming is final for every word — the last placeholder was settled as rephase (2026-07-26, formerly shiftBoundary; together with the batch confirmation F51 of 2026-07-09, no placeholders remain. §5.4). The lexis of date and width literals is settled (ADR-28/43, §5.5).

3.9 Calendar entities (ADR-35)

The entity given to the calendar: member (TSE, JPCal, etc.) is a premise that has the reserved public word nonWorking. There is no dedicated declaration syntax — the syntax for bundling “stream bindings + source:/asof:” already exists in premise definitions (§3.2) and the layer-crossing rule (end of §3.6, ADR-26); the only thing specific to calendar entities is the designation of “which binding is the non-working set”.

premise TSE {
  calendar-system: Gregorian          # needed to resolve weekday (the definition of satSun)
  tz:     "Asia/Tokyo"                # fixes which civil day a non-working "day" is (inner-fixed, ADR-33)
  source: "jpx.co.jp/trading-calendar"
  asof:   2026-01-05

  satSun     = everyDay |> filter(d => weekday(d) == Sat or weekday(d) == Sun)
  holidays   = [2026-01-01, 2026-01-12, …] covering: 2026..2026
  nonWorking = satSun | holidays      # reserved public word = the entity's "identity"
}

premise JP {
  calendar-system: Gregorian
  calendar:        TSE                # only a premise publishing nonWorking may stand here
  tz:              "Asia/Tokyo"
  wkst:            Mon
}

3.9.1 The fine-grained layer — business-hours supply conventions and standard derivations (ADR-41)

Besides nonWorking, an entity may declare the paired reserved public words sessionOpens and sessionCloses — the opening sequence and the closing sequence (time-stream type, argument-less) (optional; the declaration is a pair = declaring only one is a static error; under with derivation the judgment includes inheritance). The points are facts in the civil coordinates of the entity’s tz (wall clock — the enforcement form of “whether DST shifts them or not is a decision of the premise layer’s culture”; the rules are the civil grid of a time-of-day anchor (§3.6, ADR-31 revision 2) or civil-time widths with strideBy; the exceptions are compositions of time-of-day tables). A session = the union of half-open intervals [open_i, close_i). Overnight sessions (crossing days) are legal; 24-hour operation is outside this vessel (express it at the day-granularity layer).

Consistency check (data-relative layer; at first use of a fine-grained derivation): over the domain = joint effective coverage ∩ materialization range, adjacent markers must alternate in kind (an isolated close/open at an edge is legal as a notch). For a simultaneous open/close both points must exist, and their order is unique from the alternation requirement (close→open = continuous operation / open→close = zero width) — the handling of firing for simultaneous events belongs to the firing layer (out of scope).

Standard derivations (reserved where calendar: is in scope, when entity C declares the pair): bizOpen = the points of C.sessionOpens whose opening day (a civil day in C’s tz) is a business day of C; bizClose = each session’s closing point; isOpen(t) = whether t lies in the union of the bizOpen sessions’ intervals (a value predicate). The derivations are entity-relative — the decision inputs (holidays, tz) resolve in the entity’s culture and do not depend on the reader’s premise (a different role from day-granularity bizDay = consumer-relative). A session’s business-day-ness is read from its opening day (an overnight session’s tail follows its opening day). Coverage follows the witness rule’s three branches (true = an unannotated decision; out-of-coverage = the decision depends on annotated intervals of sessionOpens/sessionCloses/nonWorking; false = coverage complete. §4.10). No bizHour-style point sequence is derived — granularity is the expression’s choice (hourly |> filter(t => isOpen(t))).