日本語 · English

Kairos Language Specification — 2. Types and Layers

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

2.1 The two-layer structure

Kairos consists of two layers (corresponding to SQL’s DDL/DML).

Both layers share the same stream vocabulary, but their types are distinct. The premise layer absorbs the complexity; the body layer stays thin.

2.2 The three types

The time-stream type (the body layer’s first-class value)

A lazy, infinite, ordered sequence of points on the base Chronos. Equal definitions are equivalent. This is the extension (the generated sequence of points), and the body layer’s only first-class value. Within the body layer the type is kept to this single one.

The premise type (intension — generation rules)

The generation rules themselves — calendar systems, calendars, and the like. If the time-stream type is the extension, the premise type is its intension. There are primitive and derived definitions (§3.6/§3.7); derivation is a premise → premise closure, staying closed within the premise type. Primitive and derived share the same type.

The value type (numbers, booleans, enums, lists, strings, instants)

Values that are not time streams: numbers, booleans, enums (Mon, Preceding, etc.), lists ([…], indexing, and the membership predicate in), strings ("Asia/Tokyo"; the values of TZs and provenance, ADR-32), and instants (the value of a date literal; the point a lambda binds has the same makeup. A bare value binding d0 = 2026-05-15 is also legal = ADR-43/F97 — this removes the duplication of writing the same date twice, in anchor:/from: and in a table). Used in the rules of calendar-system definitions (leap-year tests, etc.; §3.6) and in body-layer arguments (the n of shift(n)). The third type, alongside the time-stream type and the premise type. A list whose elements are instants is promoted to a time-stream constant (table literals, §3.8; ADR-26. The empty list [] is promoted only with a covering: postfix = the empty table, ADR-45).

Evaluating a premise yields a time stream (never the reverse). The two layers are asymmetric, with a single conduit at the boundary where the base coordinates (asof, TZ, granularity scale, data phase; §3.8) fix the premise. The overall picture of the three types and the two layers (the diagram includes the closure of §2.3 and the one-way hierarchies of §2.4):

flowchart TD
  subgraph P["premise layer — intension (generation rules); absorbs the complexity"]
    PRIM["primitive definitions (Gregorian etc.)<br/>build calendar systems from the base"]
    DERIV["derived definitions (fiscal calendars, entity overrides, etc.)<br/>premise → premise closure"]
    DATA["data bindings<br/>(table literals, external; §3.8)"]
    PRIM -- "with overrides (cannot be expanded back into primitives = one-way)" --> DERIV
  end
  P == "evaluation — the single conduit at the boundary (asof, TZ, granularity scale, data phase fix the premise)<br/>never the reverse: a stream cannot yield a premise" ==> ST
  P -. "premise arguments (the axes of on:/unit:/axis:; in-scope calendar:)" .-> CORE
  subgraph B["body layer — extension (sequence of points); kept thin"]
    ST["time-stream type — lazy, infinite, ordered<br/>the body layer's only first-class value"]
    CORE["core family (generators, point transforms, combinators,<br/>filters, windows, selectors, strides)"]
    SUGAR["sugar (monthEnd, businessDays, etc.)"]
    SUGAR -- "erasable by expansion into core (one-way)" --> CORE
    ST -- "input" --> CORE
    CORE -- "output — no escape from the type (I2 closure)" --> ST
  end
  VAL["value type — numbers, booleans, enums, lists, strings, instants"]
  ST -- "binding-name projection name(d) = point → value (§4.9)" --> VAL
  VAL -- "window-instance reference W(v) = value → point sequence (§2.7, ADR-42)" --> ST
  VAL -. "arguments (the n of shift, predicates, label expressions)" .-> CORE
  VAL -. "instant lists promote to constants (§3.8)" .-> DATA

2.3 Closure

Every operator is (stream…, premise) → stream. There is no escape from the type. This is what enables the composition existing languages lacked — “taking the derived dates as a basis and building yet another definition on top of them.” Instead of one monolithic expression, you write a pipe in which each stage takes the previous stage’s output as its input.

2.4 Two symmetric hierarchies (core/sugar, primitive/derived)

The same pattern — separating complexity from shorthand — appears in both layers.

2.5 Symbols: one-to-one with three roles

No symbol carries more than one role. Stage connection, namespace reference, and stream union are assigned to separate symbols, one-to-one.

Symbol Role
\|> Stage connection (flows the time stream into the next stage; in the premise layer, connects premise → premise)
. Premise qualification (hierarchical namespace reference; Gregorian.month)
\| Combinator (stream union; paired with intersection & and difference \; §4.5)

2.6 Invariants

Properties the language upholds structurally (details in 10-domain-model.md and 20-adr/).

2.7 Typing rules for application — position-dependent name interpretation (ADR-42)

The same name receives different interpretations depending on where it appears and on the types of its arguments. One rule governs all of it: narrow the candidate set by the expected type of the occurrence position; if more than one interpretation survives the narrowing, never choose silently = ambiguity error (the same facet as ADR-17 and ADR-35 decision 4). This principle has three concrete faces.

Position Kind of name Interpretation Details
Axis position (on:/unit:/axis:) premise name Determining the entity’s identity + rereading it as the standard derivation §3.9, ADR-35 decision 4
Application, point argument W(d) binding with a label source Binding-name projection (point → value) §4.9, ADR-30/34/39
Application, value argument W(v) window binding with a label source Window-instance reference (value → point sequence) = preimage §4.9, ADR-42

Dispatch (deciding point versus value) branches uniquely on the type of the argument expression. The type of a lambda variable is fixed by the context of its binding site (the lambda of filter/label: binds a point; the lambda of span binds a window ordinal = a number; value functions get theirs from the typing of the body), and the decision moment is after sugar expansion and after actual-argument binding (the same computation moment as alignment, §4.5) — at that moment the type of the actual argument is always unique, so an ambiguity error can structurally never fire at an application position. A free wrapper (f = v => year(v)) has its type fixed per call (polymorphism is allowed). W(v) is legal in every stream-expecting position (head position, combinator operands, on:/unit:, the S slot of coincides, etc.); a pure value position (x = year(2020) + 1) is a type error (streams cannot mix into value expressions = the type separation of §2.2). An instant is a member of the value type (§2.2, ADR-43), and dispatch reads “point → projection; any non-point value → instance reference” — a date literal and a value variable holding a point both branch to projection (year(2026-05-15) = year(d0) = projection = 2026).