Skip to content

Reference 05 Scalars

github-actions[bot] edited this page Oct 15, 2026 · 2 revisions

5. Scalars

Property types: text, integer, long, decimal, double, boolean, Date, DateTime. Their semantics are grounded in Kotlin's corresponding types — Kotlin is the first transpilation target, and a scalar behaves as Kotlin's type unless this document states otherwise: integer is Int, long is Long, double is Double, and decimal is BigDecimal. The temporal types are java.time: Date is LocalDate, and DateTime is Instant — a timestamp is a point on the global timeline; a wall-clock moment with a zone attached is domain data, not a scalar. now is a DateTime; today is a Date — the current date in the system's one declared time zone (harness-configured in v0 — the milestone scope statement, ## Open / unresolved).

Examples in this document also use Money. It is not a builtin: it stands in for an author-extended data type — extending the scalar vocabulary is a planned, post-v0 capability (## Open / unresolved).

The builtin function vocabulary is closed — a validator can't typecheck calls into an open-ended list, so what ships is the list, and growth is ordinary vocabulary expansion rather than redesign. v0 ships: the aggregates and selectors count, sum, latest, first (## Predicate expressions), the text predicates contains, starts with, ends with, and like (## Predicate expressions), lowercase, max/min, the duration constructors seconds/minutes/hours/days/weeks (written as duration literals — ## Predicate expressions), and the generator randomUUID (initially position only). A trailing ? marks a property optional (processedOn: Date?); properties are required by default.

initially gives a property a starting value, and it is allowed on any stored property of a shape, whatever its type, relationships included: applied: boolean initially false. (On an act, initially and timestamp declarations are compile errors — every stored field there is the committer's, and a system-maintained value lives on the shape a rule materializes; ## Open / unresolved, "External input".) The field stays stored and assignable (## Assignment (mutation in place)); the initializer is evaluated once, at the instance's creation commit — so submittedOn: Date initially now records commit time as model data. It is an optional clause on a stored declaration, not a separate property kind — but it is distinct from derivation: applied: boolean = false in shape-body position would mean derived, always false — unassignable (## Derived properties). The two places initially can't appear follow from those properties' own rules, not from a restriction on the clause: a derived property has no stored value to initialize, and a timestamp field's value is commit metadata, never author-supplied (below). Its refinement-position sibling is captured, which evaluates at the entry commit instead of the creation commit (## Refinements).

initially also accepts a generator: processorKey: text initially randomUUID mints a unique value at the creation commit — how a system-originated act gets an outward-facing key (## rule, "Transactions and after commit"). Like now, a generator is evaluated once, at the creation commit, and a rolled-back-and-retried transaction re-evaluates it. Which generators exist beyond randomUUID is open (## Open / unresolved); sequence-flavored generators are excluded deliberately — ordering comes from timestamps or declared fields, never minted values.

timestamp declares a property whose value is commit metadata — populated by the commit itself, never by a committer or a rule — with the persistence behavior part of the declaration:

shape Review {
    product: one Product
    stars: integer
    body: text
    createdAt: timestamp on create
    updatedAt: timestamp on update
}

on create fixes the value at the instance's creation commit; on update advances it at every commit that writes a stored field of the instance (creation included, so the field is never absent). The names are the author's — correctedOn: timestamp on create is fine. The values are never committer-suppliable (a fact about the commit can't be claimed by the client) and never assignable (this.updatedAt = now is a compile error) — which is the point: updatedAt maintained by hand would mean every writing rule also assigns it, boilerplate that lies the first time a rule forgets. Contrast initially now: also evaluated at the creation commit, but it declares ordinary stored business data, assignable ever after (a submission date an admin may later correct); timestamp on create declares unassignable bookkeeping fact. Choose by whether the moment is a business datum or a fact about the commit. A declared creation timestamp is also the natural datum to name in a selector's by clause (## Predicate expressions) — but it is consulted only by name, never implicitly.

Every instance carries id — identity as a readable value, present with no declaration. An act declared by carries one more language-populated property, its actor, the committer the binding handed in, under the name the clause binds (actor, or the as name): never supplied, never assigned, stored on a durable act like a timestamp, and readable through any reference to the act (§22, "The actor"). It is unique per instance, fixed at creation, never assignable, and supports == and nothing else: not ordered, not arithmetic, not parseable text, so a spec can never depend on representation — whether id is a UUID, an auto-increment, or an existing table's some_unique_id primary key supplied at an integration boundary is compilation's business (## 1. Principles). id is a reserved name; a domain whose business vocabulary includes an "id" (an externally meaningful account number) declares its own field for it — business identifiers are data, id is identity. Instance comparison was always identity comparison (## Predicate expressions); id is its readable form: a == b is a.id == b.id. One deliberate asymmetry with timestamp fields: those are declared surface with language-populated values, while id appears in no declaration — identity is the one fact every record carries whether or not the author writes it down.

Clone this wiki locally