State-transition specifications and adaptive clients #
Specifications describe mathematical states, typed operations, relational transitions, and input
admissibility. Clients use Quang Dao's upstream PFunctor.FreeM; continuations may depend on
earlier answers. The state interpreter and its transcripts have no computational-model or
performance claim. In particular, pure Lean computation in continuations is not charged as a
machine instruction.
The relational semantics is demonic for callers and angelic for implementations:
- An adaptive client
p : D.Client αis demonically admissible (D.DemonicValid s p) when each issued operation is admissible (D.valid) and the continuation remains admissible under every outcome(s', a)permitted byD.step. - Specifications whose
stepis the graph of a functionf, with exactly one outcomef s ofor every(s, o), admissible or not, are constructed viaStateTransitionSpec.ofFnand recognized by theStateTransitionSpec.HasStepFntypeclass. It impliesStateTransitionSpec.DeterministicandStateTransitionSpec.Total, but not conversely: those two together still allow an inadmissible input to have no outcome. It equips these specifications with functional execution helpers (StateTransitionSpec.stepFn,StateTransitionSpec.toFLTS,StateTransitionSpec.run,StateTransitionSpec.ValidHistory,StateTransitionSpec.totalCost,StateTransitionSpec.runClient,eval) and equivalence lemmas connecting the relational and functional views.
Main definitions #
StateTransitionSpec: relational stateful operation semantics with typed answers and admissibility.StateTransitionSpec.Total,StateTransitionSpec.Deterministic: totality on admissible inputs and transition determinism.StateTransitionSpec.toLTS: the underlying labelled transition system (Cslib.LTS) of input-output transitions.StateTransitionSpec.Step,StateTransitionSpec.Transcript,StateTransitionSpec.ValidTranscript,StateTransitionSpec.ValidOpSeq,StateTransitionSpec.totalCostTranscript,StateTransitionSpec.totalCostOutcome: execution transcripts, demonic operation-sequence admissibility, and transcript cost accounting.StateTransitionSpec.signature,StateTransitionSpec.Client,StateTransitionSpec.call,StateTransitionSpec.ClientRun,StateTransitionSpec.DemonicValid: adaptive clients, relational client runs, and demonic client admissibility.StateTransitionSpec.ofFn,StateTransitionSpec.HasStepFn,StateTransitionSpec.stepFn: constructor and typeclass for specifications given by a transition function.StateTransitionSpec.toFLTS,StateTransitionSpec.run,StateTransitionSpec.ValidHistory,StateTransitionSpec.totalCost,StateTransitionSpec.runClient,StateTransitionSpec.clientResult,StateTransitionSpec.clientState,StateTransitionSpec.clientHistory,eval: functional execution and history interpretation under[StateTransitionSpec.HasStepFn].product: independent abstract components; concrete independence is an implementation obligation.
Main results #
StateTransitionSpec.Transcript.labels_map_fst,StateTransitionSpec.ValidTranscript.append,StateTransitionSpec.totalCostTranscript_append: transcript projection and composition laws.StateTransitionSpec.exists_validTranscript_of_validOpSeq,StateTransitionSpec.DemonicValid.exists_clientRun: existence of valid runs under[StateTransitionSpec.Total].StateTransitionSpec.ClientRun.bind,StateTransitionSpec.clientRun_bind_iff,StateTransitionSpec.DemonicValid.bind,StateTransitionSpec.DemonicValid.of_bind: sequential composition and decomposition for relational adaptive clients.StateTransitionSpec.validOpSeq_iff_validHistory,StateTransitionSpec.validTranscript_iff_run,StateTransitionSpec.demonicValid_iff_validHistory_runClient,StateTransitionSpec.clientRun_iff_runClient: equivalence of relational and functional semantics under[StateTransitionSpec.HasStepFn].StateTransitionSpec.fst_run_eq_toFLTS_mtr,StateTransitionSpec.run_append,StateTransitionSpec.validHistory_append,StateTransitionSpec.totalCost_append,StateTransitionSpec.run_runClient,StateTransitionSpec.runClient_bind,StateTransitionSpec.clientResult_bind,StateTransitionSpec.clientState_bind,StateTransitionSpec.clientHistory_bind,StateTransitionSpec.clientResult_pure,StateTransitionSpec.clientState_pure,StateTransitionSpec.clientHistory_pure,eval_bind,eval_eq_liftM: functional history and client laws under[StateTransitionSpec.HasStepFn].StateTransitionSpec.fst_snd_runClient_bind,StateTransitionSpec.snd_snd_runClient_bind,StateTransitionSpec.fst_snd_runClient_pure,StateTransitionSpec.snd_snd_runClient_pure: the state and history laws in the form thatsimpindexes. WithclientResult_bind,clientResult_pure,run_runClient,totalCost_append,validHistory_appendandPFunctor.FreeM.bind_eq_bindthey form the simp setclient_simps:simp only [client_simps]splits the result, final state, history, cost and admissibility of a sequential client at its binds.
References #
A relational abstract state machine with operation-dependent observations.
step s o s' a holds when transitioning from s to s' and returning a : Result o is a
permitted outcome of operation o. Only admissible operations (valid s o) are constrained by
refinement certificates.
- State : Type u
Mathematical states, independent of a storage representation.
- Op : Type v
Operations available to clients.
The observation type returned by each operation.
Relational state transition and observation specification.
Preconditions under which an operation's implementation must satisfy its contract.
Instances For
The set of permitted (nextState, result) outcomes of operation o from state s.
Instances For
A specification is total on admissible inputs if every valid (s, o) admits at least one
permitted outcome (s', a).
- step_total (s : D.State) (o : D.Op) : D.valid s o → ∃ (s' : D.State) (a : D.Result o), D.step s o s' a
Every admissible operation call has at least one permitted post-state and result.
Instances
Obtain a permitted outcome (s', a) for an admissible operation under [D.Total].
A specification is deterministic if each (s, o) admits at most one outcome (s', a).
- step_deterministic {s : D.State} {o : D.Op} {s₁ : D.State} {a₁ : D.Result o} {s₂ : D.State} {a₂ : D.Result o} : D.step s o s₁ a₁ → D.step s o s₂ a₂ → s₁ = s₂ ∧ a₁ = a₂
Any two permitted outcomes for the same state and operation coincide.
Instances
The polynomial operation signature, shared with upstream effectful programs.
Instances For
An adaptive client: each continuation may depend on the observed answer. Inductive clients have no infinite execution branch; branching need not be finite.
Instances For
Invoke a single operation o : D.Op in an adaptive D.Client.
Instances For
An input-output transition label pairing an operation o : D.Op with its result a.
Instances For
Transcripts, non-adaptive operation sequences, and cost accounting #
A finite execution transcript is a list of recorded steps.
Instances For
Extract the underlying sequence of operations from a transcript.
Instances For
Extract the Cslib.LTS input-output labels from a transcript.
Instances For
Projecting the first component of Transcript.labels recovers Transcript.ops.
D.ValidTranscript s tr s' holds when tr is a valid step-by-step execution starting at s
and ending at s', where each step is admissible (D.valid) and permitted by D.step.
- nil {D : StateTransitionSpec} (s : D.State) : D.ValidTranscript s [] s
- cons {D : StateTransitionSpec} {s : D.State} (st : D.Step) {tr : D.Transcript} {s_fin : D.State} : D.valid s st.op → D.step s st.op st.nextState st.result → D.ValidTranscript st.nextState tr s_fin → D.ValidTranscript s (st :: tr) s_fin
Instances For
The empty transcript is valid iff the initial and final states coincide.
A nonempty transcript is valid iff its head step is admissible and permitted and its tail is
valid from st.nextState.
Valid transcripts compose across concatenation.
Every valid transcript projects to a multi-step transition in D.toLTS.
Demonic admissibility of a non-adaptive operation sequence ops : List D.Op from s:
the head operation is admissible at s, and the tail sequence remains admissible from every
permitted post-state s' of the head operation.
Instances For
On a specification where every operation is valid in every state, every operation sequence is demonically admissible.
Demonic operation-sequence admissibility splits across concatenation across all valid transcripts of the prefix.
Under [D.Total], every demonically admissible operation sequence admits at least one valid
transcript.
State-dependent charges summed along a transcript.
Instances For
Outcome-dependent charges summed along a transcript, where each step charge may also inspect
the post-state st.nextState and result st.result.
Instances For
totalCostTranscript is the special case of totalCostOutcome that ignores the post-state and
result.
Along a valid transcript prefix D.ValidTranscript s tr₁ s₁, totalCostTranscript splits
additively at the intermediate state s₁.
Uniform per-operation bounds scale by the transcript length on any valid transcript.
A constant charge K per operation totals tr.length * K.
A unit charge per operation totals the transcript length.
Adaptive clients (ClientRun and DemonicValid) #
D.ClientRun s p v s' tr holds when running the adaptive client p : D.Client α from initial
state s can produce return value v : α, final state s' : D.State, and transcript tr by
making admissible and permitted transitions at each operation call.
- pure {D : StateTransitionSpec} {α : Type y} (s : D.State) (a : α) : D.ClientRun s (PFunctor.FreeM.pure a) a s []
- liftBind {D : StateTransitionSpec} {α : Type y} {s s₁ s₂ : D.State} (o : D.Op) (k : D.Result o → D.Client α) (a : D.Result o) {v : α} {tr : D.Transcript} : D.valid s o → D.step s o s₁ a → D.ClientRun s₁ (k a) v s₂ tr → D.ClientRun s (PFunctor.FreeM.liftBind o k) v s₂ ({ op := o, result := a, nextState := s₁ } :: tr)
Instances For
Every ClientRun produces a valid transcript.
Sequential client runs compose by concatenating their transcripts.
A client run of p.bind k splits into a run of p and a run of k from the intermediate
result and state.
Demonic admissibility of an adaptive client p from state s:
.pure ais always admissible;.liftBind o krequiresD.valid s onow, and for every permitted outcome(s', a)ofofroms, the continuationk amust be demonically admissible froms'.
- pure {D : StateTransitionSpec} {α : Type y} (s : D.State) (a : α) : D.DemonicValid s (PFunctor.FreeM.pure a)
- liftBind {D : StateTransitionSpec} {α : Type y} {s : D.State} (o : D.Op) (k : D.Result o → D.Client α) : D.valid s o → (∀ (s' : D.State) (a : D.Result o), D.step s o s' a → D.DemonicValid s' (k a)) → D.DemonicValid s (PFunctor.FreeM.liftBind o k)
Instances For
On a specification where every operation is valid in every state, every client is demonically admissible.
Under [D.Total], every demonically admissible client admits at least one valid ClientRun.
Demonic admissibility composes across bind when the continuation is demonically admissible
after every valid run of the first client.
Demonic admissibility of p.bind k splits: p is demonically admissible, and so is the
continuation after every valid run of p. This is the converse of DemonicValid.bind.
Specifications given by a transition function (StateTransitionSpec.ofFn, #
StateTransitionSpec.HasStepFn) and functional helpers
Construct a deterministic StateTransitionSpec from a pure transition function
stepFn : (s : State) → (o : Op) → State × Result o and an optional admissibility predicate
valid.
Instances For
Typeclass witnessing that a specification D : StateTransitionSpec is governed by a pure
transition function f : (s : D.State) → (o : D.Op) → D.State × D.Result o: the only outcome of
o from s is f s o, whether or not D.valid s o holds.
- step_iff (s : D.State) (o : D.Op) (s' : D.State) (a : D.Result o) : D.step s o s' a ↔ (s', a) = f s o
A transition
D.step s o s' aholds if and only if(s', a) = f s o.
Instances
The functional transition (s : D.State) → (o : D.Op) → D.State × D.Result o of a
specification D with [D.HasStepFn f].
Instances For
The empty history is admissible in every state.
A nonempty history is admissible when its first operation is valid and the tail is admissible from the next state.
On a specification where every operation is valid in every state, every history is admissible.
The cost of a nonempty history adds the first operation's charge to the tail cost from the next state.
Charges compose at the intermediate abstract state. The element type D.Op is not indexed, so
simp also uses this for a concrete specification such as Memory.spec w, whose Op reduces.
Uniform operation bounds imply a bound proportional to history length.
Interpret a client mathematically, retaining its chosen operation history.
Instances For
Final abstract state reached by a deterministic client run D.runClient s p.
Instances For
Operation history selected by a deterministic client run D.runClient s p.
Instances For
Interpreting a pure client returns its value, preserves the state, and records no operations.
Interpreting a single lifted operation steps the specification once.
Interpreting an operation call steps the specification and continues from the resulting state and answer.
Interpreting a lifted operation bound to a continuation steps the specification and continues from the resulting state and answer.
Interpreting a lifted operation monadically bound to a continuation steps the specification and continues from the resulting state and answer.
Admissibility splits at the mathematical state reached by the first history. The element type
D.Op is not indexed, as in totalCost_append.
Sequential interpretation concatenates the histories actually selected by the client.
A sequential client answers as its continuation, run from where the first client stopped.
A sequential client stops where its continuation, run from where the first client stopped, stops.
The history of a sequential client is the first client's history, then its continuation's.
A client that only returns a value answers with it.
A client that only returns a value leaves the state unchanged.
clientState and clientHistory unfold to a match on D.runClient s p. Under its
metavariables simp cannot reduce that match, so it would index clientState_bind and
clientHistory_bind under a key that no goal has; in a goal it sees (D.runClient s p).2.1. The
following restatements have that form, and put the simp set client_simps in the normal form of
clientState and clientHistory.
clientState_bind, stated in the form that simp indexes.
clientHistory_bind, stated in the form that simp indexes.
clientState_pure, stated in the form that simp indexes.
Under [D.HasStepFn f], demonic operation-sequence admissibility
ValidOpSeq coincides with D.ValidHistory.
The unique canonical transcript determined by a specification D with [D.HasStepFn f] on
an operation list os from state s.
Instances For
The underlying operation list of D.canonicalTranscript s os is os.
The labels of D.canonicalTranscript s os coincide with (D.run s os).2.
totalCostTranscript along D.canonicalTranscript s os equals D.totalCost charge s os.
Every admissible operation history executes canonically to (D.run s os).1.
Under [D.HasStepFn f], ValidTranscript s tr s' collapses
uniquely to D.ValidHistory s tr.ops, s' = (D.run s tr.ops).1, and
tr = D.canonicalTranscript s tr.ops.
Under [D.HasStepFn f], demonic client admissibility
DemonicValid s p is equivalent to D.ValidHistory s (D.clientHistory s p).
Every client with an admissible deterministic history executes canonically.
Under [D.HasStepFn f], ClientRun collapses uniquely to
D.clientResult, D.clientState, D.clientHistory, and D.ValidHistory.
Evaluating a single lifted operation steps the state and returns its answer.
Evaluating a lifted operation bound to a continuation steps the state and continues with the observed answer.
Evaluating a lifted operation monadically bound to a continuation steps the state and continues with the observed answer.
eval agrees with interpreting the free monad into StateM D.State via PFunctor.FreeM.liftM
when states, operation answers, and client results share a universe.
Two independent abstract components. A backend must separately justify their independence.
The answer types are Sum.elim D.Result E.Result, so the signature of a product depends only on
the signatures of its components: clients of product D E are clients of product D' E' when
D, D' and E, E' differ only in their states' admissibility.
Instances For
Stepping the left component of a product specification steps the left state and preserves the right state.
Stepping the right component of a product specification preserves the left state and steps the right state.
Instances on products #
product is an abbrev, so an instance stated on product D E would be indexed by the unfolded
structure, whose state type is the projection D.State × E.State. A nested product such as
product (product D E) F has state type (D.State × E.State) × F.State after unfolding, which
that index does not match. The product instances therefore index their specification with
no_index, and products of any depth find them by unification.
Product of two total specifications is total.
Product of two deterministic specifications is deterministic.
The canonical transition function of a product of two specifications with transition
functions fD and fE.
Instances For
A product of specifications with HasStepFn has HasStepFn with the componentwise transition
function, at any nesting depth.
The product of two ofFn specifications, with their transition functions named. It is tried
before instHasStepFnProduct, so such products keep this form of the transition function.