DKE Python — Language Reference
Version: draft Status: Published reference manual (companion to the DKE Python Language Specification). Audience: Tenants writing DKE Python — the programmer’s day-to-day lookup for verbs, types, statements, operators, and classes.
1. About this reference
This is the reference manual for DKE Python, the language a DKE tenant writes and submits to the DKE service at dke.langsyn.net. It is a companion to the DKE Python Language Specification (SPEC_python.md), not a replacement for it.
- The Specification is normative. It defines conformance — grammar, static and dynamic semantics, the error model, versioning — and is the authority a compiler author implements against.
- This Reference is a lookup manual. It answers “what does this verb do, what does it produce, how do I write it?” one construct at a time, with a worked example for each.
Every entry that states a rule cites the governing specification section as → Spec §N. Where this Reference and the Specification appear to differ, the Specification governs — tell us, because it is a documentation bug.
The language and this document are open under the MIT license. The DKE service that runs DKE Python is LangSyn’s proprietary product — the language is published; the implementation is not.
DKE Python is a Python-flavoured surface over DKE’s knowledge-operation semantics: def declarations, bare assignment, match/case, try/except … as, and an explicit self receiver. It is indentation-significant. Source files use the extension .dpy.
2. A program at a glance
A DKE Python source file is a module: one or more def declarations. Every declaration is stored under its own name and is independently invocable.
# greet.dpy — a one-declaration module.
def greet(K: string, s: string):
remember(K.s.mood, "cheerful", "op1") # write a claim
m = current(K.s.mood) # read it back (bare assignment)
match m: # consume the discriminator
case active_claim:
log("mood = " + m.value)
case empty:
log("no mood recorded")
Reading the shape:
def greet(K: string, s: string):— a declaration (§7.1): thedefkeyword, name, typed parameters (name: type), a:and an indented suite.remember(…),current(…)— verb calls (§5): the built-in verb surface written as function calls with a dottedK.s.aslot path as the first argument. The natural-language phrase form (remember K.s.mood = "cheerful" per "op1",current K.s.mood) is an accepted equivalent.match/case— a statement (§7.8): exhaustive discriminator dispatch. (Python usescase; the base member useswhen.)m.value— field access (§10) on anactive_claim.
Invoke it at the wire surface with invoke greet("Cat", "felix"); in another program, call it with a plain call greet(K, s) (there is no invoke keyword).
Learn from inside the language. Send a unit whose body is --help for orientation, or --help <topic> for one of header, types, verbs, statements, scripts.
--help verbs
3. Natural language
A source has no header line: it is the program body from its first line. The natural language a source is written in — which keyword vocabulary its statements use, and the language its responses render in — is set out of band, by the language parameter supplied when the source is submitted, not by the source. → Spec §3.9
- When the
languageparameter is omitted, the language is English (eng).engis the default and, at this time, the only offered value; a request for another natural language is declined. - A program’s meaning is independent of the language it is written in: the same program in any supported keyword vocabulary denotes the same thing. The natural language is a property of how you talk to the service — set per call — not a property stored in the program.
4. Types
DKE Python has a closed, flat type vocabulary shared across the family: no subtyping, no generics, no user-defined record types. Programs compose by operating on the store through verbs. → Spec §5
4.1 The 17 types
| Type | Produced by | Category | Notes |
|---|---|---|---|
string |
literals, user data, values | primitive | no ordering; == / != only |
int |
literals, .length, counts |
primitive | signed 64-bit |
bool |
comparison + logical results, True / False |
primitive | |
value |
active_claim.value (a claim’s stored content) |
tagged union | int | bool | string | datetime | duration | real | null | blob; consume with match (§7.8) |
datetime |
datetime("…Z") literal, a datetime value read |
value class | a UTC instant; renders as RFC 3339; a write-verb value + case datetime as d: |
duration |
duration("…") literal, a duration value read |
value class | a length of time; renders as ISO 8601 (PnDTnHnMnS); a write-verb value + case duration as u: |
real |
3.5 literal, a real value read |
value class | a fractional number, written 3.5 (§3.5.7); renders as its decimal text in canonical form; computes under + - * / + ordered comparison (§8.3); a write-verb value + case real as r: |
blob |
blob("…") / blob_b64("…") literal, a blob value read |
value class | an opaque byte string, written blob("…") or blob_b64("…") (§3.5.8); renders as canonical uppercase hex; a write-verb value + case blob as b: |
active_claim |
current K.s.a, a field read |
discriminator | one live claim, or empty |
claim_list |
get, list values of, caveats of, dependents of, conflicts, diff … since |
finite collection | element active_claim |
proof_tree |
why K.s.a |
finite collection | element active_claim (the derivation’s participating claims); also field .root |
subject_set |
subjects where, list subjects of |
finite collection | element string |
string_list |
list categories, list attributes of, list checkpoints, list pinned, list scripts |
finite collection | element string |
handle |
checkpoint <name> |
opaque token | field .name |
verify_result |
verify … = v |
discriminator | fields .match, .actual, .expected |
result_type_set |
list result types |
finite collection | element string |
empty |
the second arm of an active_claim match |
discriminator sentinel | case empty: only, not a type name |
Three groupings drive the static rules:
- Primitives —
string,int,bool. - Finite collections —
claim_list,string_list,subject_set,result_type_set,proof_tree. Exactly the types afor … inmay iterate (§7.7) and the types with a.length(§10).claim_listandproof_treeyieldactive_claimelements; the other three yieldstring. Aproof_treeadditionally offers a.rootaccessor (§10.2). - Discriminators —
active_claim(+ itsemptycase) andverify_result. Consumed by amatch, alongside thevalueunion. - The
valueunion — the stored content of a claim (active_claim.value), one of eight classes:int,bool,string,datetime,duration,real,null, orblob. Seven are write-producible —int,bool,string,datetime,duration,real,blob(a program writes one and reads it back at that class). The remaining class,null(an explicit absent value), is read-only: it is asserted viaassert(§5.1), no value literal produces it, but a claim carrying one is discriminable on read. Consume the union with a valuematch(§7.8) to recover the typed content, or render it directly. → Spec §5.6 datetime— a UTC instant written with thedatetime("…Z")literal (e.g.datetime("2026-01-02T03:04:05Z"), RFC 3339 with a requiredZand optional 1–9 fractional-second digits). A write verb stores it typed and it reads back as thedatetimeclass ofvalue; it renders as its RFC 3339 text. → Spec §3.5.5duration— a length of time written with theduration("…")literal (e.g.duration("PT1H30M"), an ISO 8601 durationPnDTnHnMnS; calendar years and months are not accepted). A write verb stores it typed and it reads back as thedurationclass ofvalue; it renders as ISO 8601 in a canonical form, soduration("PT90M")reads back asPT1H30M. → Spec §3.5.6real— a fractional number written as a decimal literal3.5(at least one digit on each side of the point;_may separate digits as in1_000.5;3.and3.namestay an integer3). A write verb stores it typed and it reads back as therealclass ofvaluein a canonical decimal form (insignificant trailing zeros are dropped —3.50reads back as3.5, while5.0reads back as5.0); it renders as its decimal text. Acase real as r:arm bindsr : real. Reals compute:+ - * /and the ordered comparisons accept them and promoteint → real, and any real operand makes/true division (§8.3, §6.3). → Spec §3.5.7blob— an opaque byte string written as a hex literalblob("48656C6C6F")(an even count of hex digits) or a base64 literalblob_b64("SGVsbG8=")(RFC 4648; the same bytes) — two input spellings for one byte space; the emptyblob("")/blob_b64("")is a valid zero-byte blob. A write verb stores it typed and it reads back as theblobclass ofvaluein a single canonical uppercase-hex form regardless of the input spelling (a value writtenblob_b64("SGVsbG8=")reads back48656C6C6F); it renders as its hex text. Acase blob as b:arm bindsb : blob. → Spec §3.5.8
There is no absence-of-value type and no null literal. To assert that a value is known to be absent, use assert K.s.a per src (§5.1), which carries no value. A parameter may be annotated with any value type in this section (none of empty, value, datetime, duration, real, or blob is a parameter type — empty is a match arm only, value arises only from .value field access, and each value class arises only from its literal or a case … as …: arm: datetime from a datetime("…Z") literal or case datetime as d:, duration from a duration("…") literal or case duration as u:, real from a 3.5 literal or case real as r:, and blob from an blob("…") / blob_b64("…") literal or case blob as b:). → Spec §4.2, §5
4.2 Handler bindings
The except refuse as <var> / except engine_error as <var> handlers of a try (§7.9) bind a value exposing string fields (for a refuse, .reason and other string fields; for an engine error, .reason). These cannot be written as parameter types. → Spec §5.4
5. Verbs
The built-in verb surface is written as function calls — a verb name and a parenthesized, comma-separated argument list: remember(K.s.a, v, src), current(K.s.a), list_attributes(K). The first argument is the dotted K.s.a slot path (or a name literal / handle, per the verb). This call form is the canonical surface. Read verbs bind by assignment (cur = current(K.s.a)), not base’s let. → Spec §7, §4.5
The phrase form is an accepted equivalent. Every verb may also be written as a natural-language phrase — the head-word, the slot path, and where needed the prepositions of / where / since / per (remember K.s.a = v per src, current K.s.a, list attributes of K). The two forms are interchangeable and compile to the identical program; a source may mix both. Each entry below shows the canonical call form first, with the phrase form noted alongside; §5.8 tabulates both spellings per verb.
Path slots: K (kind), K.s (kind + subject), K.s.a (full cell), K.a (kind + attribute). The head K is a string-typed parameter or binding in scope, not a bare literal.
The call form’s compound heads (list_attributes, list_values, list_subjects, list_categories, list_result_types, list_checkpoints, list_pinned, list_scripts) are single snake_case tokens — soft keywords recognized as a verb only immediately before (, so the same spelling stays a usable ordinary identifier elsewhere.
5.1 Write verbs (statement position)
remember(K.s.a, v, src) · phrase: remember K.s.a = v per src
Records value v at cell K.s.a attributed to src. Permissive. The value is a scalar literal (string / int / bool) and is stored with its type — the claim reads back through .value at the type it was written (§4.1, value), and query verbs match it type-aware (subjects, verify). An optional pair of trailing datetime bounds gives a validity window — the fact is valid only between them (both inclusive); either bound may be null for an open side. Omit the pair for an always-valid fact. A windowed write is selected by an as-of read (current/get with a datetime("…")) whose time falls in the window.
remember(K.s.color, "orange", "op1") # string
remember(K.s.count, 42, "op1") # int — reads back as int
remember(K.s.rate, "3.0", "src", datetime("2019-01-01T00:00:00Z"), datetime("2021-01-01T00:00:00Z")) # valid 2019–2021
remember(K.s.plan, "active", "ops", datetime("2024-01-01T00:00:00Z"), null) # valid 2024 onward, open end
update(K.s.a, v, src) · phrase: update K.s.a = v per src
Supersedes the claim; establishes one on an empty cell. Accepts the same optional trailing datetime validity window as remember.
update(K.s.mood, "playful", "op2")
forget(K.s.a) · phrase: forget K.s.a
Retracts a claim. Path-scoped — no value- or source-scoped form.
forget(K.s.mood)
assert(K.s.a, src) · phrase: assert K.s.a per src
Asserts the value at K.s.a is known to be absent per src. No value.
assert(K.s.chip_id, "vet")
pin(K.s.a) · phrase: pin K.s.a
Marks the active claim (does not protect it from a later forget).
pin(K.s.color)
5.2 Read verbs (expression position)
current(K.s.a) → active_claim · phrase: current K.s.a
The single live claim, or the empty case. Consume with match or .value. An optional datetime reads the value as of a past time: current(K.s.a, t"…").
cur = current(K.s.color)
get(K.s.a) → claim_list · phrase: get K.s.a
The full claim history. Iterable. An optional datetime reads the claims valid as of a past time: get(K.s.a, t"…").
hist = get(K.s.mood)
caveats(K.s) → claim_list · phrase: caveats of K.s
Caveat claims attached to subject K.s.
cav = caveats(K.s)
conflicts() → claim_list · phrase: conflicts
All currently conflicting claims. (No slot argument.)
confs = conflicts()
list_values(K.a) → claim_list · phrase: list values of K.a
Every value claim for attribute K.a across subjects.
vals = list_values(K.color)
subjects(K.a, v) → subject_set · phrase: subjects where K.a = v
Subjects of kind K whose attribute a currently equals v. The value v is a scalar literal (string / int / bool) and the match is type-aware: subjects(K.a, 42) matches the int 42, not the text "42" (Spec §7.2).
hits = subjects(K.color, "orange") # text match
warm = subjects(K.temp, 42) # int match — not the text "42"
list_subjects(K.a) → subject_set · phrase: list subjects of K.a
Every subject of kind K with a claim for attribute a.
subs = list_subjects(K.color)
list_attributes(K) / list_attributes(K.s) → string_list · phrase: list attributes of K / of K.s
Attribute names for a kind, or for one subject.
attrs = list_attributes(K)
list_categories() → string_list · phrase: list categories
Every kind (category) known to the store.
cats = list_categories()
5.3 Provenance and reasoning
why(K.s.a) → proof_tree · phrase: why K.s.a
The derivation of the current value. The returned proof_tree is an iterable finite collection of the claims that participated in the derivation: .length, for … in, and [i] yield those claims, each an active_claim carrying .value / .source / .trust / .created_at (§4.1, §10.3). .root still yields the top active_claim — the queried claim.
pt = why(K.s.color)
n = pt.length # how many claims participated
r = pt.root # the queried claim
first = pt[0] # a participating claim → active_claim
for c in pt: # iterate the derivation's claims
log("via " + c.source)
verify(K.s.a, v) / verify(K.s.a, v, src) → verify_result · phrase: verify K.s.a = v [per src]
Checks whether K.s.a currently holds v (optionally per src). The value v is a scalar literal (string / int / bool) and the check is type-aware: .match is true only when the stored value and its type agree with v (Spec §7.2). Fields .match (bool), .actual, .expected (string).
v = verify(K.s.color, "orange")
if v.match:
log("confirmed")
dependents(<handle>) → claim_list · phrase: dependents of <handle>
Claims that depend on a checkpoint handle’s state.
deps = dependents(h)
5.4 Checkpoints
checkpoint("<name>") → handle (statement or expression) · phrase: checkpoint <name>
Captures a restore point. In the call form the name is a string literal; in the phrase form it is a bare identifier.
h = checkpoint("before_cleanup")
restore(<handle>) (statement) · phrase: restore <handle>
Reverts the store’s current view to the point the checkpoint captured: every cell written after the checkpoint returns to its checkpoint-era value (a later update is undone, a later forget is reinstated). The store is append-only, so restore does not erase history — it records the reversion as new claims, so current reverts while get (full history) keeps growing. An unknown handle is refused.
restore(h)
diff(K.s.a, <handle>) → claim_list · phrase: diff K.s.a since <handle>
Claims at K.s.a that changed since the checkpoint.
changed = diff(K.s.color, h)
list_checkpoints() → string_list · list_pinned() → string_list · phrases: list checkpoints / list pinned
Names of live checkpoints; paths of pinned cells.
cps = list_checkpoints()
pins = list_pinned()
5.5 Result-type introspection
list_result_types() → result_type_set · phrase: list result types
The set of read-result-type names.
rts = list_result_types()
5.6 Script-data reads (in-language)
Read-only, so they run inside a program. → Spec §7
list_scripts() → string_list · phrase: list scripts
Bare names of every stored script.
names = list_scripts()
5.7 Wire-only verbs (not callable in a program)
Callable at the dke.langsyn.net wire surface but rejected in a program body — they mutate the script set or answer administrative provenance. → Spec §7
| Wire form | What it does |
|---|---|
import <module> |
load a stored module by name (also runs in-language, §7.11) |
import <name> from "<source>" |
provide + store + run a module (§7.11) |
import <name> from "<source>" as curated |
provide a module into the shared library (§7.11) |
invoke <name>(<args>) |
execute a stored script |
list scripts |
list signatures (also runs in-language) |
forget script <name> |
remove a script (idempotent) |
info script <name> |
compile provenance |
The lifecycle verbs are edition-neutral. import <name> from "<source>" is the submission form: it stores a module by name and runs its body — which matters for a module that writes data or installs rules at its top level (§7.11). → Spec §11.1, §17.3
5.8 Call form and phrase form, side by side
The call form is canonical; the phrase form is an accepted equivalent. Both compile to the identical program, and a source may mix them. → Spec §7.1
| Call form (canonical) | Phrase form (accepted) | Position | Result |
|---|---|---|---|
remember(K.s.a, v, src) / remember(K.s.a, v, src, t"…", t"…") |
remember K.s.a = v per src [t"…" t"…"] |
stmt | (void) |
update(K.s.a, v, src) / update(K.s.a, v, src, t"…", t"…") |
update K.s.a = v per src [t"…" t"…"] |
stmt | (void) |
forget(K.s.a) |
forget K.s.a |
stmt | (void) |
assert(K.s.a, src) |
assert K.s.a per src |
stmt | (void) |
pin(K.s.a) |
pin K.s.a |
stmt | (void) |
checkpoint("<name>") |
checkpoint <name> |
stmt or expr | handle |
restore(<handle>) |
restore <handle> |
stmt | (void) |
current(K.s.a) / current(K.s.a, t"…") |
current K.s.a [t"…"] |
expr | active_claim |
get(K.s.a) / get(K.s.a, t"…") |
get K.s.a [t"…"] |
expr | claim_list |
why(K.s.a) |
why K.s.a |
expr | proof_tree |
caveats(K.s) |
caveats of K.s |
expr | claim_list |
dependents(<handle>) |
dependents of <handle> |
expr | claim_list |
conflicts() |
conflicts |
expr | claim_list |
diff(K.s.a, <handle>) |
diff K.s.a since <handle> |
expr | claim_list |
verify(K.s.a, v) / verify(K.s.a, v, src) |
verify K.s.a = v [per src] |
expr | verify_result |
subjects(K.a, v) |
subjects where K.a = v |
expr | subject_set |
list_subjects(K.a) |
list subjects of K.a |
expr | subject_set |
list_attributes(K) / list_attributes(K.s) |
list attributes of K / of K.s |
expr | string_list |
list_values(K.a) |
list values of K.a |
expr | claim_list |
list_categories() |
list categories |
expr | string_list |
list_checkpoints() |
list checkpoints |
expr | string_list |
list_pinned() |
list pinned |
expr | string_list |
list_result_types() |
list result types |
expr | result_type_set |
list_scripts() |
list scripts |
expr | string_list |
5.9 Aggregate query functions
A read-only fold over a whole column — one attribute across every subject of a kind — returning a value (§4.1). Bind it to a name on its own line, then use it; an aggregate may not be a bare argument to another call. → Spec §7.1.1
n = count(Reading.value) # single-column fold
p90 = percentile(Reading.value, 90) # ranked fold, rank 0–100
slope_xy = slope(Point.x, Point.y) # two-column fold (both columns of one kind)
mean = avg(Reading.value) # bind first, then use `mean`
- Single-column
fn(K.col)—count,sum,product;avg,median,midpoint,mode,geomean,harmmean,rms;min,max,span,variance,stdev,iqr,mad,cv;skewness,kurtosis,entropy. - Ranked
percentile(K.col, <int>)— the<int>-th percentile (rank 0–100). - Two-column
fn(K.x, K.y)(both columns of one kind) —covariance,correlation,slope,intercept,rsquared.
The result’s class follows the column (a numeric column yields a number; a datetime / duration column a datetime / duration); destructure with match or use it wherever a value is expressible. Fold names are soft keywords — recognised only right before (, so they stay ordinary identifiers elsewhere. An aggregate over a column with no data is a refuse; a numeric fold over a column whose values are not numbers (a text column, or one mixing numbers with non-numeric values) is likewise a refuse — the fold declines rather than treating a non-number as zero. count counts values of any class, so it never refuses on that ground. → Spec §7.1.1, §10.3
6. Operators
DKE Python uses word-form logical operators (and / or / not), the comparisons == != < > <= >=, and the arithmetic / concatenation operators + - * / %. → Spec §8
6.1 Comparison
== != over (string, string), (int, int), (bool, bool), two discriminators (compares .kind), or a value against a scalar / another value (Python-faithful — a class mismatch is false, never a coercion; §4.1); < > <= >= over numeric operands (int or real, mixed allowed — compared numerically). No string ordering.
6.2 Logical
and, or (short-circuit), not, over bool.
6.3 Arithmetic
+ - * / over numeric operands (int or real); % (remainder) over int only; prefix - negates an int. The numeric operators promote: int × int stays int, and any real operand yields a real. Division is the one place the boundary shows — int / int is integer division (truncating toward zero), but any real operand makes / true division (7 / 2 is 3, 7.0 / 2 is 3.5). Reals compute to IEEE-754-double precision and render in canonical decimal form. Division / modulo by zero and out-of-range (including non-finite real) results refuse at runtime. → Spec §8.3
6.4 String concatenation
+ joins a string with a string, int, bool, or value (either order); the non-string operand renders (a value by its class). The only implicit coercion.
log("count = " + xs.length)
6.5 Field access and indexing
x.field (see §10) and xs[i] (zero-based; out-of-range refuses; index is int). → Spec §5.2, §5.3
7. Statements
Every statement is one logical line, or a :-headed block with an indented suite. Indent with spaces only. → Spec §4.3
7.1 Declaration
def audit(K: string, s: string):
<suite>
The def keyword, name, typed parameters (name: type), :, indented body. Two declarations in a unit must not share a name; a unit compiles its declarations in order and a call may target only an earlier one (compile-before-call). → Spec §6.8
7.2 Assignment (bind a read)
hist = get(K.s.color)
Bare assignment — there is no let introducer. The right side must not be a statement-only verb (no value). → Spec §6.4
7.3 log
log(<expr>) # canonical
log <expr> # accepted alternative
Appends <expr>’s rendering to the transcript, written as a call — log("temp=" + t.value) — or, equivalently, without parentheses; the two forms are identical. The argument is string, int, bool, or a discriminator. → Spec §8.7
7.4 Script call (invocation)
audit_subject(K, s)
A plain call of a stored script — there is no invoke keyword (a script is called like any function). Static, compile-time inline expansion; argument count and types must match; no recursion, no cycles; no value-returning invocation. → Spec §6.8, §9.7
7.5 Verb-call statement
A write verb (§5.1) in statement position:
remember(K.s.color, "orange", "op1")
7.6 if
if <cond>:
<suite>
else:
<suite>
<cond> must be bool; else is optional. → Spec §9.2.1
7.7 for … in
for c in hist:
<suite>
Iterates a finite collection only; any other iterand is a type_error. The collection is snapshotted at entry. Bounded iteration is what gives DKE Python its termination guarantee. → Spec §6.3, §9.5
7.8 match (discriminator dispatch)
match cur:
case active_claim:
log("value = " + cur.value)
case empty:
log("absent")
The scrutinee must be a discriminator (active_claim, verify_result). Arms exhaustively cover the discriminator’s cases. Python uses case where the base member uses when. → Spec §6.2
(A match whose scrutinee is a bare instance variable is the distinct class match, §8.6.)
A match whose scrutinee is a value (a claim’s .value, §4.1) is a value match: the case labels are the eight value-classes int, bool, string, datetime, duration, real, null, and blob. The seven write-producible classes (int, bool, string, datetime, duration, real, blob) may bind the typed content with a named as <var> arm; the one read-only class, null, is a bare arm only — attaching as <var> to it is a type_error:
match cur.value:
case int as n: log("next = " + (n + 1)) # n : int
case string as s: log("text = " + s)
case duration as u: log("span = " + u) # u : duration
case real as r: log("value = " + r) # r : real — renders decimal
case blob as b: log("bytes = " + b) # b : blob — renders hex
case null: log("an absent value") # bare — no `as`
case default: log("other class")
Arms cover all eight classes or provide case default:; a bound as <var> is typed at the arm’s class and scoped to that arm. → Spec §5.6, §6.2
7.9 try
try:
<suite>
except refuse as r:
<suite>
except engine_error as e:
<suite>
Python’s own except … as …: shape. Both handlers are optional. A refused statement does not bind and transfers control to except refuse as; an engine error to except engine_error as. With no matching handler, the outcome propagates. → Spec §9.2.4
7.10 branch / commit / rollback
with branch("experiment"):
remember(K.s.color, "tigret", "audit")
if ok:
commit
else:
rollback
A with branch("<name>"): block groups writes transactionally: clean exit commits; an uncaught refuse / engine error rolls back. Bare commit / rollback end the branch explicitly. Ordinary invocation is not transactional. → Spec §9.2.5, §7.5
7.11 import (module)
import catalog # in a program body: load a stored module
A DKE Python file is a module (Spec §17); import <module> loads one: it runs the module’s body once and makes its names available under the module’s namespace. import is a contextual keyword — the import statement only at statement head, an ordinary identifier elsewhere. A module may import another module; an import cycle is rejected when the module loads. Loading a module already loaded reconciles it: only the cells whose values changed are rewritten, so re-import is safe to repeat. → Spec §17.1, §17.3, §17.5
A module’s names are namespaced. A kind a module declares with class is addressed under the module name — catalog.Product — and a cell of it is read with the module-qualified construction module.Kind(subject).field:
p = catalog.Product("widget").price # read catalog's Product widget, field price
log("price = " + p.value)
Two modules may each declare a Product without collision (catalog.Product, store.Product, distinct). → Spec §17.6
Provide a module from the wire. import <name> from "<source>" stores a module by name and runs it; import <name> from "<source>" as curated provides it into the shared library on the import search path, where a caller’s own module of the same name shadows it. These two forms are wire-managed (§5.7); the bare import <module> is the form written in a program. → Spec §17.3, §17.4
Data at module scope. A write verb (§5.1) written at a module’s top level — not inside a def — is a data declaration: it runs when the module is imported. A module that only declares a schema, installs a rule, or writes data defines no callable script and is loaded for its body’s effect. → Spec §17.2, §17.7
# in a module body, at top level:
remember(Rate.vat.pct, 25, "2026-finance-act") # runs on import
8. Classes, methods, and reasoning
Classes group typed fields with the methods over them and give a typed handle — an instance — for one named member (§8.1–8.6). The section then covers the derivation features — computed fields (§8.7), standing rules (§8.8), and hypothesis (§8.9) — where the engine records or supposes values on your behalf. The class surface itself adds no new types, no new verbs, and no new runtime behaviour; termination is preserved throughout. class and return are keywords. → Spec §14
8.1 Class declaration
class Sensor:
temp: string # typed field (primitive)
def record(self, v: string, src: string): # void method (explicit self)
remember(self.temp, v, src)
def label(self) -> string: # value method
return "sensor"
Written class <Name>: — no marker. Fields are name: type with a primitive type; names unique within the class. Declaring a class stores nothing. → Spec §14.1
8.2 Instance construction and binding
s = Sensor(room)
Sensor(room) names one member of Sensor identified by the string room; bound by assignment, its type is the class name. Construction records nothing. Reading a never-written field yields empty. → Spec §14.2
8.3 Field read and write
reading = s.temp # read → active_claim or empty
remember(s.temp, "21", "op1") # write value + provenance source
A read has type active_claim; consume with match or .value. Accessing a field the class does not declare is a type_error. Inside a method the explicit receiver self (the first parameter) names the instance (self.temp). → Spec §14.3, §14.4
8.4 Void method
s.record("21", "op1") # statement position; self is supplied by the receiver
def name(self, params): — no return type; performs actions; called as a statement. The self argument is supplied by the receiver, not written at the call. → Spec §14.4
8.5 Value method
name = s.label() # expression position
def name(self, params) -> type: ending with return <expr> of that type; called in expression position. Static dispatch from the receiver’s declared class; compile-before-call; no recursion. → Spec §14.4
8.6 Class match (dispatch by class)
match s:
case Sensor:
remember(s.temp, "22", "op1")
case Actuator:
remember(s.state, "on", "op1")
A match whose scrutinee is a bare instance variable dispatches over class names. Inside case <Class>: the instance is treated as that class. The arm set is closed and exhaustive — cover the instance’s declared class or provide a case default:. Non-exhaustive or unknown-class arms are type_errors. Resolved from the compile-time class — no runtime dispatch. → Spec §10.5
8.7 Computed fields
A class field declared with an initializer the engine derives from the class’s other fields and keeps current as they change. The customer writes only the inputs; the derived field reads back like any field, with a derived provenance in place of a source. → Spec §14.7
class Order:
price: int
qty: int
total: int = price * qty # int: an arithmetic expression (+ - * %) over earlier fields
big: bool = total > 1000 # bool: one ordered comparison of an earlier field
An int field is arithmetic (+ - * %) over earlier fields; a real field may also use /; a bool field is one ordered comparison (< <= > >=). Every reference is to a field declared earlier, so computed fields never form a cycle, and writing an input re-derives every field built on it. why over a computed field lists the inputs it was derived from (§5.3). → Spec §14.7
8.8 Standing rules
A named top-level declaration — a sibling of class / def, not a class member — that derives a field automatically: whenever its condition holds the engine records the conclusion, and it withdraws the conclusion when the condition stops holding. The @rule decorator marks the def as a rule; absent names its one negative premise, inside a rule block only. → Spec §15
@rule
def needs_review():
for o in Order:
if o.total > 1000 and absent(o.shipment):
o.review = True
for <var> in <Kind>— the rows the rule ranges over and the row variable.if <premise> (and <premise>)*— each premise is a comparison (o.total > 1000, a field against a number,< <= > >=) or an absence (absent(o.shipment), the row has no value recorded for that field).- the conclusion
<var>.<field> = <literal>is recorded with aderivedprovenance; the customer never writes it directly.
A rule must name which rows it applies to (at least one comparison, not an absence alone) and must not cancel itself out — either is rejected when the rule is defined. It ranges over finitely many rows; termination is preserved. A derived field reads back like any other (current / get). → Spec §15
8.9 Hypothesis
suppose(<field>, <value>, <field>) reads what the second field would be if the first were set to the value — letting standing rules and computed fields run under the supposition — then discards everything. Nothing persists. Returns a value; bind it and read it, or concatenate it into a log. → Spec §16
would = suppose(Order.o1.total, 2000, Order.o1.review) # true, if a rule sets review over 1000
Supposes exactly one fact. If the read field has no value under the supposition, suppose produces no value — a catchable refuse (guard it with try / except). Safe for preview, comparison, and what-if exploration; termination preserved. → Spec §16
9. Errors
Two static diagnostic kinds; runtime adds two catchable outcomes. → Spec §10
| Category | Stage | Recoverable in source? |
|---|---|---|
parse (parse_error) |
static — token structure | no |
type (type_error) |
static — semantic rule | no |
refuse |
runtime — verb call | yes, via except refuse as |
engine_error |
runtime — service layer | yes, via except engine_error as |
Common triggers:
- parse — tab in indentation or a bad dedent; unterminated string; reserved word (
class,return, …) as an identifier; an empty module (whitespace and comments only). - type — undeclared identifier; a wire-only script verb in a program body; a non-exhaustive
match; aforover a non-collection; a type mismatch; a non-existent field. refuse— the store rejects a well-formed request: out-of-surface slot, wrong slot count, index out of bounds, division / modulo by zero, integer overflow,commitoutside a branch.engine_error— the service composed a well-formed request that failed at the engine layer.
10. Accessors and field tables
10.1 Universal accessors → Spec §5.2
<expr>.kind → string— for any discriminator and forproof_tree.<expr>.length → int— for anystringor finite collection.
10.2 Field access by type → Spec §5.2
| Type | Field | Field type |
|---|---|---|
active_claim |
value |
value (union; §4.1, Spec §5.6) |
active_claim |
source |
string |
active_claim |
claim_id |
string (positional path#n, not stable) |
active_claim |
created_at |
string (RFC 3339 UTC µs; empty for pre-stamping claims) |
active_claim |
trust |
string |
proof_tree |
root |
active_claim |
verify_result |
match |
bool |
verify_result |
actual |
string |
verify_result |
expected |
string |
handle |
name |
string |
10.3 Indexing element types → Spec §5.3
| Collection | [int] yields |
|---|---|
claim_list |
active_claim |
proof_tree |
active_claim |
string_list |
string |
subject_set |
string |
result_type_set |
string |
11. Keywords
Structural keywords are English in both language surfaces (Python does not localise keywords): def class return match case if else for in try except as branch commit rollback and or not log True False. Verb head-words (remember, current, get, …) and their prepositions (of, where, since, per) are the shared surface (§5).
import (§7.11) is a contextual keyword — the import statement only at statement head, an ordinary identifier elsewhere. Type names (string, int, …) are likewise contextual, not reserved — but avoid all of these as identifiers.
12. Quick reference card
DECLARE def name(K: string, n: int): # `def`; name: type params
ASSIGN hist = get(K.s.a) # bare assignment (no let)
LOG log("x = " + n) # string|int|bool|discriminator
# call form is canonical; the phrase form (remember K.s.a = v per src, …) is accepted
WRITE remember(K.s.a, v, src) update(K.s.a, v, src) forget(K.s.a)
assert(K.s.a, src) pin(K.s.a)
READ current(K.s.a) -> active_claim get(K.s.a) -> claim_list
why(K.s.a) -> proof_tree verify(K.s.a, v[, src]) -> verify_result
subjects(K.a, v) -> subject_set list_attributes(K) -> string_list
caveats(K.s) conflicts() list_values(K.a) list_subjects(K.a)
CHECKPT h = checkpoint("name") restore(h) diff(K.s.a, h)
LISTS list_categories() list_checkpoints() list_pinned() list_scripts() list_result_types()
SCRIPT list_scripts() -> string_list
TYPES string int bool | active_claim verify_result empty (discriminators)
claim_list proof_tree string_list subject_set result_type_set (collections) | handle
OPS == != < > <= >= (int cmp) and or not (bool) + - * / % (int)
+ (string concat) .field [i]
IF if c: … / else: …
FOR for c in list: … # finite collections only
MATCH match d: case active_claim: … / case empty: …
TRY try: … / except refuse as r: … / except engine_error as e: …
BRANCH branch b: … commit / rollback
CALL name(args) # plain call; no `invoke`
CLASS class Sensor:
temp: string
def record(self, v: string, src: string): remember(self.temp, v, src)
def label(self) -> string: return "sensor"
INSTANCE s = Sensor(room) remember(s.temp, "21", "op1") s.record("22", "op1")
reading = s.temp name = s.label()
match s: case Sensor: … / case default: …
ERRORS parse type (static) refuse / engine_error (runtime, catchable)
HELP --help [header|types|verbs|statements|scripts]
13. Python tooling and type checking
DKE Python is a Python-syntax surface: a standard Python parser accepts .dpy, so formatters (black), linters (ruff / flake8), language servers, and syntax highlighting work on .dpy files as they stand.
For type checkers (pyright / mypy), which additionally want every name in scope, DKE ships a stub, dke.pyi, declaring the builtin surface — the verbs, the value factories, the type names, the @rule decorator, and the branch context manager, none of which the source imports. A checked copy that opens with the one-line prelude from dke import * then type-checks; the service compiles the original headerless source unchanged.
The core surface (verbs, slot paths, values, control flow, rules, aggregates, transactions) checks clean. Two DKE constructs mean something different to a Python checker than to the DKE service — kinds-as-classes construction (Sensor(room)) and the case <type> as <var>: type match — so relax the codes they trigger (the dke.pyi header lists the exact settings). The DKE service remains the authority on types.
Companion document: DKE Python — Language Specification (SPEC_python.md), the normative authority. This Reference is informative; where the two differ, the Specification governs.