Persistent OcaltQL

Two independent mechanisms let state survive beyond a single script execution: PERSISTENT stores values that survive across requests; START PERSIST opens a standing execution timeline with no time limit.

Implementation pending. Both mechanisms are designed and specified below. Runtime implementation is in progress.

PERSISTENT — Cross-Request Values

SET PERSISTENT stores a value that survives across separate script executions, with no expiry. It persists until it is overwritten. This is fully independent of START PERSIST — it works with no standing timeline open at all.

SET PERSISTENT ?var is nothing more than ordinary variable setting with a persistent capture target. Everything you already know from Variables SET | UNSET applies unchanged — the only difference is the word PERSISTENT sitting in front of the captured ?var. Both capture forms carry straight over: the generic SET PERSISTENT ?var AS value and the typed STRING "x" SET PERSISTENT ?var (or NUMBER, CALCULATE, and every other value-producing verb) are two spellings of the same thing, and both land the same value in persistent storage under that name.

Set a Persisted Value — Generic SET Form
SET PERSISTENT ?pvar AS "some value"
Set a Persisted Value — Typed Verb Target Form
STRING "this" SET PERSISTENT ?var
Persisting a Number
NUMBER 1000 SET PERSISTENT ?numbervar
Persisting a Computed Value
CALCULATE 10 + 5 SET PERSISTENT ?total
Overwriting a Persisted Value
STRING "hello" SET PERSISTENT ?var
AFTER STRING "world" SET PERSISTENT ?var
(* The persisted value is now "world" — the same overwrite behaviour as an ordinary SET *)
Variable-to-Variable, into Persistent Storage
STRING "hello" SET ?source
AFTER SET PERSISTENT ?copy AS ?source
(* ?copy is persisted, carrying ?source's value — the generic AS form doing var-to-var *)
Checking a Persisted Value Exists
STRING "hello" SET PERSISTENT ?var
AFTER IF ?var IS SET
OPEN
  EMIT "still here"
CLOSE
OR
OPEN
  EMIT "expired or cleared"
CLOSE
A persistent variable is always written as persistent. PERSISTENT sits in front of the variable everywhere it appears — on the way in with SET PERSISTENT ?pvar, and on the way out with EMIT PERSISTENT ?pvar. A bare ?pvar is an ordinary script-local variable and has nothing to do with the persisted one, even under the same name. Nothing is guessed from context; the word is what makes it persistent.
Reading a Persisted Value — EMIT PERSISTENT
STRING "Hello World!" SET PERSISTENT ?pvar
AFTER EMIT PERSISTENT ?pvar
A Persistent and an Ordinary Variable of the Same Name
STRING "persisted" SET PERSISTENT ?var
AFTER STRING "local" SET ?var
AFTER EMIT ?var
AFTER EMIT PERSISTENT ?var
(* Output: local, then persisted — two separate variables *)
UNSET has no persistent counterpart — there is no UNSET PERSISTENT. Everything else from Variables SET | UNSET — declaring, overwriting, the SET ... AS and typed forms, the IS SET check — mirrors directly by placing PERSISTENT on the variable.
Read It Later — A Separate Script Execution
EMIT PERSISTENT ?pvar

Persistent Operations

An operation persists the same way a value does. NEW PERSISTENT OPERATION registers it once, across executions; RUN PERSISTENT calls it from any later script without redefining it. Everything from Operations & Return carries over unchanged — parameters, typed parameters, RETURN, the isolated frame — the only difference is the word PERSISTENT.

Define It Once
NEW PERSISTENT OPERATION calculate_total WITH ?price AS NUMBER REQUIRED
OPEN
  CALCULATE ?price * 1.15 SET ?total
  AFTER RETURN ?total
CLOSE
Call It From Any Later Script
RUN PERSISTENT calculate_total WITH 100 SET ?result
AFTER EMIT ?result
(* The definition is not in this script at all — it was registered earlier *)
The same rule as variables. PERSISTENT is written on both halves — NEW PERSISTENT OPERATION to register, RUN PERSISTENT to call. A plain RUN opname only ever finds an operation defined in the current script, so a persistent operation and a script-local one of the same name never collide.

START PERSIST / KILL PERSIST — A Standing Execution Timeline

START PERSIST is a separate, distinct mechanism from SET PERSISTENT. Where SET PERSISTENT stores individual values, START PERSIST opens a standing execution timeline with no time limit at all — think of it as turning OcaltQL into a live terminal session. Once started, every later script execution in the same session continues within that same open timeline, carrying its state forward, until KILL PERSIST explicitly closes it.

Open a Standing Timeline
START PERSIST
Close It
KILL PERSIST
PERSISTENT and START PERSIST are unrelated mechanisms. Named values and operations that outlive a script, versus a standing execution timeline that carries a whole session forward until explicitly killed. Neither requires the other to be active.
PERSISTENT is not a substitute for PROMISE. PROMISE/WAIT FOR handles asynchronous resolution within a single script’s own live execution. PERSISTENT only carries an already-settled value across separate, later script executions — it has no in-flight, live async-resolution behavior of its own.

A Terminal, Built From PERSIST and !QUERY

A standing timeline plus !QUERY is enough to build a real terminal. Inside an open PERSIST timeline an ordinary ?var already carries forward from one submission to the next — nothing has to be marked persistent, because the timeline itself is what is holding the state. !QUERY hands each submission its own source text, so the session can record what was typed.

Open the Session
START PERSIST
AFTER NEW ARRAY SET ?history
AFTER EMIT "session open"
Every Later Submission Records Itself
APPEND !QUERY TO ?history
AFTER CALCULATE 40 + 2 SET ?answer
AFTER EMIT ?answer
(* ?history is still the same array from the first submission — the timeline kept it *)
Read the History Back
COUNT ?history SET ?n
AFTER EMIT ?n & " commands this session"
AFTER FOREACH ?history SET ?i AS ?cmd
OPEN
  EMIT ?i & ": " & ?cmd
CLOSE
Close It
EMIT "closing, " & [COUNT ?history] & " commands run"
AFTER KILL PERSIST
(* ?history goes with the timeline *)
No PERSISTENT appears anywhere in this example, and that is the point. PERSIST holds the whole execution state open; PERSISTENT stores one named value independently of any timeline. A terminal needs the first, not the second.

Sessionless Persistence

CONSISTENT is the session-blind counterpart to PERSISTENT. PERSISTENT is scoped to the session — a value stored during one of your users’ sessions is only ever visible to that same session. CONSISTENT stores one value for the whole namespace: every script in your namespace reads and writes the same value, whichever of your users happens to be running it.

“Global” means global to your namespace, and no further. A CONSISTENT value lives inside your namespace exactly as your files and databases do — no other namespace can see it, read it, or write to it. The isolation guarantee that applies to everything else applies here unchanged.
Setting a Consistent Value
STRING "Hello World!" SET CONSISTENT "key"
Reading It — From Any Script, Any Session
CONSISTENT "key" SET ?var
AFTER EMIT ?var
AFTER EMIT [CONSISTENT "key"]
(* Both forms read the same global value — direct SET capture, or inline reference *)
Page View Counter — Sessionless Global Persistence
(* Any script in the namespace, any visitor, any session — all increment the same counter *)
CONSISTENT "page_views" SET ?views
AFTER IF ?views IS NULL OPEN NUMBER 0 SET ?views CLOSE
AFTER ADD 1 TO ?views SET ?views
AFTER STRING ?views SET CONSISTENT "page_views"
AFTER EMIT "This page has been viewed " & ?views & " times"