338 lines
9.8 KiB
Markdown
338 lines
9.8 KiB
Markdown
# View Contracts and View Trees
|
|
|
|
## 1. Purpose
|
|
|
|
View Contracts are the portable checking layer for Tree Calculus programs.
|
|
|
|
The checker does not consume detached metadata about a separate executable. Its
|
|
canonical input is a typed, checkable tree artifact: ordinary tree data that
|
|
contains both the executable program payloads and the view/contract structure
|
|
needed to validate and transform them.
|
|
|
|
The checker consumes this artifact and returns either:
|
|
|
|
```text
|
|
checked-execution artifact
|
|
```
|
|
|
|
or:
|
|
|
|
```text
|
|
structured diagnostic
|
|
```
|
|
|
|
A checked-execution artifact is interpreted by `runChecked`. Unguarded programs
|
|
are represented as `checkedPure rootPayload`; guarded programs contain explicit
|
|
checked guard/bind nodes.
|
|
|
|
This keeps checking independent of any particular host implementation. A typed
|
|
artifact may be produced by any frontend, compiler, hand-written generator, or
|
|
future self-hosted `tricu` toolchain.
|
|
|
|
## 2. Design Principle
|
|
|
|
The model follows the same discipline as interaction trees.
|
|
|
|
Interaction trees use tagged structural envelopes with explicit executable
|
|
payloads:
|
|
|
|
```tri
|
|
io action = pair "tricuIO" (pair version action)
|
|
pure x = pair 0 x
|
|
bind action k = pair 1 (pair action k)
|
|
```
|
|
|
|
The interpreter understands the outer structure, but it does not recursively
|
|
mistake every subtree for interpreter metadata. A continuation `k` is an opaque
|
|
executable tree until the interpreter reaches the `bind` step that applies it.
|
|
|
|
View trees use the same rule:
|
|
|
|
```text
|
|
structure says how to check;
|
|
opaque executable fields are only executed/applied by the checker at the
|
|
appropriate step.
|
|
```
|
|
|
|
This is the key distinction that allows Views to carry guards without confusing
|
|
ordinary program trees with View metadata.
|
|
|
|
## 3. Views
|
|
|
|
A View is an extrinsic contract over an ordinary Tree Calculus value. Tree
|
|
Calculus values do not carry native runtime types; a View describes how a value
|
|
may be treated by the checker or by a checked boundary.
|
|
|
|
Core View forms:
|
|
|
|
```text
|
|
Any
|
|
Ref ref
|
|
Fn [argView...] resultView
|
|
List elemView
|
|
Maybe elemView
|
|
Pair leftView rightView
|
|
Result errView okView
|
|
Guarded baseView guard
|
|
```
|
|
|
|
`Ref` supports both generated/numeric and symbolic references. Symbolic refs are
|
|
preferred for user-authored views:
|
|
|
|
```tri
|
|
UserId = viewRef "UserId"
|
|
```
|
|
|
|
A guarded view refines a base view with an executable guard:
|
|
|
|
```tri
|
|
UserId = viewGuarded (viewRef "UserId") userIdGuard
|
|
```
|
|
|
|
The guard is ordinary program code. The View validator checks that the guarded
|
|
view envelope is well-formed, and recursively validates the `baseView`, but it
|
|
must treat the guard payload/reference as opaque executable data, not as another
|
|
View.
|
|
|
|
## 4. Guards
|
|
|
|
Guards are ordinary `tricu` values/functions grouped with the Views they refine.
|
|
|
|
Example:
|
|
|
|
```tri
|
|
userIdGuard = value :
|
|
-- ordinary program that validates value
|
|
|
|
UserId = viewGuarded (viewRef "UserId") userIdGuard
|
|
|
|
loadUser id@UserId = ...
|
|
```
|
|
|
|
Guards return the standard checked-runtime protocol:
|
|
|
|
```tri
|
|
guardOk value
|
|
guardFail
|
|
```
|
|
|
|
Guards do not author diagnostics. The checked-exec runner owns guard failure and
|
|
malformed-guard diagnostics using boundary context from the checked artifact.
|
|
|
|
Guards are injected by the checker. They are not discovered by the runtime as a
|
|
separate metadata layer. The checking process transforms a view tree into an
|
|
executable tree with the necessary guard applications inserted.
|
|
|
|
## 5. View Tree Artifact
|
|
|
|
The primary checker-facing artifact is a view executable term graph.
|
|
|
|
Conceptually:
|
|
|
|
```text
|
|
ViewTree
|
|
version
|
|
root node id
|
|
nodes
|
|
```
|
|
|
|
Each node is tagged tree data. Nodes combine executable payloads, view claims,
|
|
and structural relationships in one graph.
|
|
|
|
Representative node forms:
|
|
|
|
```text
|
|
Value node view executableTree
|
|
Apply node calleeNode argNode expectedOrInferredView
|
|
Require node requiredView sourceNode
|
|
External node name view
|
|
```
|
|
|
|
This is not a mandatory final encoding; it is the semantic target. The important
|
|
property is that executable trees and checking structure are carried together in
|
|
a single portable artifact.
|
|
|
|
A node may contain opaque executable fields. Those fields are tree terms, but
|
|
they are not recursively decoded as view-tree nodes or Views unless the node's
|
|
semantics explicitly says so.
|
|
|
|
## 6. Checker Semantics
|
|
|
|
The checker is an interpreter over the view tree.
|
|
|
|
For each node it may:
|
|
|
|
1. validate the node envelope;
|
|
2. validate Views referenced by the node;
|
|
3. check compatibility between expected and actual Views;
|
|
4. recursively check child nodes;
|
|
5. inject guards required by guarded Views;
|
|
6. produce the executable tree for that node;
|
|
7. memoize node results by node id.
|
|
|
|
The root node result is a checked-execution program.
|
|
|
|
In abstract form:
|
|
|
|
```text
|
|
checkViewTree : ViewTree -> Result CheckedExec Diagnostic
|
|
```
|
|
|
|
or, in self-hosted terms:
|
|
|
|
```tri
|
|
checkViewTree viewTree = ... -- ok checkedExec / err diagnostic
|
|
```
|
|
|
|
## 7. Compatibility and Guard Injection
|
|
|
|
Structural compatibility is about Views. Guard injection is about producing the
|
|
checked-execution tree.
|
|
|
|
For example, if a node is required to satisfy:
|
|
|
|
```tri
|
|
viewGuarded (viewRef "UserId") userIdGuard
|
|
```
|
|
|
|
then the checker verifies the underlying View relationship and emits executable
|
|
code that applies `userIdGuard` at the appropriate checked boundary.
|
|
|
|
The checker, not the runtime metadata system, owns this transformation.
|
|
|
|
## 8. Source Annotations
|
|
|
|
Source annotations are one frontend syntax for producing view-tree nodes.
|
|
|
|
Examples:
|
|
|
|
```tri
|
|
Nat = viewRef "Nat"
|
|
Box a = viewPair (viewRef "Box") a
|
|
|
|
idNat x@Nat =@Nat x
|
|
idBox x@(Box String) =@(Box String) x
|
|
```
|
|
|
|
Annotations are value-level View expressions. Names such as `Nat` and `Box` are
|
|
ordinary program values/functions that evaluate to Views.
|
|
|
|
A frontend that supports this syntax should lower the source into a view tree
|
|
that contains the relevant executable terms, views, and checking structure. The
|
|
artifact must not depend on source names or on the frontend implementation that
|
|
produced it.
|
|
|
|
## 9. Contract Expressions
|
|
|
|
Contract-expression helpers remain useful as authoring/building tools, but they
|
|
are not the fundamental artifact model.
|
|
|
|
Preferred style for expression-oriented authoring is pipeline-first:
|
|
|
|
```tri
|
|
mapBoolStringUse = cFn <|
|
|
[(viewFn [(viewBool)] viewString) (viewList viewBool)] (viewList viewString)
|
|
|> cApply (cFn [(viewBool)] viewString)
|
|
|> cApply (cValue (viewList viewBool))
|
|
|> cRequire (viewList viewString)
|
|
```
|
|
|
|
These helpers should be understood as convenient ways to build typed/checkable
|
|
structure, not as a permanent replacement for view-tree artifacts.
|
|
|
|
## 10. Artifact Direction
|
|
|
|
The target direction is to make the view tree the canonical checked-program
|
|
artifact.
|
|
|
|
Older split concepts remain useful internally or during development:
|
|
|
|
```text
|
|
tree term
|
|
view value
|
|
typed-program node
|
|
module/export manifest
|
|
```
|
|
|
|
But the durable design should avoid treating contracts as detached facts about a
|
|
separate program. The portable checker input is the checkable program itself.
|
|
|
|
In short:
|
|
|
|
```text
|
|
Do not store code over here and contracts over there.
|
|
Store a view tree: executable code plus the structure needed to check and guard it.
|
|
```
|
|
|
|
## 11. IO Interaction Trees
|
|
|
|
`tricu` IO is represented as ordinary interaction-tree data:
|
|
|
|
```tri
|
|
io action = pair "tricuIO" (pair version action)
|
|
pure value = pair 0 value
|
|
bind action k = pair 1 (pair action k)
|
|
```
|
|
|
|
View Contracts do not change that representation. A checked program may produce
|
|
an ordinary IO interaction tree, and the existing IO driver can execute it
|
|
unchanged.
|
|
|
|
For source evaluation with contracts enabled, `tricu eval --io` performs an
|
|
additional frontend instrumentation pass over visible IO continuations. When a
|
|
continuation returns a `pure (...)` value that mentions source-annotated
|
|
functions, the frontend lowers that pure expression into the existing portable
|
|
checked-exec protocol before returning the next IO action.
|
|
|
|
This means source sugar works for practical checked IO paths such as:
|
|
|
|
```tri
|
|
acceptNames xs@(NonEmptyList String) =@String "accepted"
|
|
|
|
main = io (bind (pure []) (xs : pure (acceptNames xs)))
|
|
```
|
|
|
|
and for explicit higher-order boundaries:
|
|
|
|
```tri
|
|
useHandler handler@(Fn [(NonEmptyList String)] String) xs@(List String) =@String
|
|
handler xs
|
|
|
|
main = io (bind (pure []) (xs : pure (useHandler acceptNames xs)))
|
|
```
|
|
|
|
The IO runtime does not perform View inference or guard injection at every step.
|
|
The source/frontend pass constructs checked-exec boundaries once; the runtime
|
|
only evaluates the resulting interaction tree.
|
|
|
|
Current limitations:
|
|
|
|
- This is source-visible instrumentation, not whole-program function-flow
|
|
tracking.
|
|
- Higher-order guarantees require explicit annotated boundaries.
|
|
- Raw prebuilt interaction trees, imported executable artifacts, and content-store
|
|
terms are not automatically re-instrumented unless they pass through this
|
|
source-lowering path.
|
|
- The IO action shape itself is only shallowly checkable unless users provide
|
|
guarded Views for the relevant boundaries.
|
|
- Continuation result Views are not inferred from external effects; dynamic IO
|
|
values should cross annotated/guarded boundaries when runtime enforcement is
|
|
required.
|
|
|
|
Making IO checking more complete is future work. In particular, a future design
|
|
may validate every continuation-produced action structurally, carry checked
|
|
wrappers with higher-order function values, or define a portable checked-IO
|
|
artifact instead of relying on Haskell/frontend source instrumentation.
|
|
|
|
## 12. Host Independence
|
|
|
|
No part of the core View Tree design is specific to Haskell or to the current implementation.
|
|
|
|
Any producer may emit a view-tree artifact if it follows the portable tree-data
|
|
encoding. Any checker implementation may consume it if it implements the typed
|
|
node semantics.
|
|
|
|
The current implementation can produce and consume these artifacts, but it is
|
|
not the semantic authority. The artifact format and the self-hosted checker
|
|
semantics are the authority.
|