Skip to content

Commit dcab86d

Browse files
committed
Compare nine compact semantics capsules and separate host sponsorship
1 parent 9182930 commit dcab86d

14 files changed

Lines changed: 521 additions & 10 deletions

‎docs/objective-bend/EMERGENT-SMALLTALK.txt‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
Emergent Smalltalk: Objective Bend + governed durable objects.
2-
Core dynamics; surface syntax encodes these terms.
2+
Core dynamics, not surface syntax.
33
Application associates left. \x.t binds x; t[u/x] avoids capture; new binders are fresh.
44
Fields/arms are ordered lists keyed by strings; lookup takes the first match.
55
Values v: \x.t, Nat, Bool, String, {fields}, spec(m,e), proto(s,t), inj(k,t).
66
All components of these values are unevaluated. Scalar types are disjoint.
7-
Types are structural, with exact row equality; fix has (C->V->C)->V->C.
7+
Rows compare exactly; recursive records are nominal; fix: (C->V->C)->V->C.
88
Specs may bound future Self/Super rows; fix discharges those bounds.
9-
Activities cannot be shared in fields/arguments/payloads. Affine/linear means
10-
at most once; reusable closures capture only unrestricted, shareable values.
9+
Activities cannot be shared in fields/arguments/payloads. Affine/linear use
10+
is at most once; terminal linear discharge is separate. Reusable captures are shareable/unrestricted.
1111

1212
(\x.t) u -> t[u/x]
1313
mix(a,b) -> \s.\p.b s (a s p)
@@ -38,16 +38,16 @@ E[perform(p)] yields p with continuation E; response r resumes as E[r].
3838
A nonvalue with no step or yield is stuck; infinite reduction diverges.
3939

4040
Every layer shares final self s; p is the whole result below it.
41-
Call-by-name meaning; runtime thunks are shared (call-by-need).
41+
Call-by-name reference; sharing requires adequacy.
4242

4343
Host object contract:
44-
Object = durable id, pinned code, versioned state, law, payer.
44+
Object = durable id, pinned code, versioned state, law.
4545
A reference names an object; it does not grant access. Reads and writes require
4646
current authority. Grants face current law; law revision is not revocation.
4747
A method runs pinned code on typed inputs and a current state view. Its result
4848
proposes effects. Commit a turn atomically only if its exact read roots still
49-
match, current authority/laws admit every write, and its budget is funded.
50-
Failed turns commit no application writes; fees may apply. No owner bypass.
49+
match and current authority/laws admit every write. No owner bypass.
50+
Failed turns commit no application writes. Funding/fees belong to the host profile.
5151
Delegation is explicit, scoped to callee, method, code and arguments.
5252
Synchronous call trees commit together. Sends enqueue durable messages and
5353
return answer slots; delivery is a later turn. A slot has one decider, deadline,
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Emergent Smalltalk capsules
2+
3+
Three presentations at each strict decimal budget: fewer than 1,000, 2,000 or
4+
3,000 UTF-8 bytes, including the title, whitespace and scope statement. All are
5+
ASCII. These are alternative descriptions of the same source semantics, not
6+
language variants. Automatafl is a separate package.
7+
8+
| Presentation | <1 KB | <2 KB | <3 KB |
9+
| --- | --- | --- | --- |
10+
| Rewrite relation and evaluation contexts | [rewrite-1k.txt](rewrite-1k.txt) | [rewrite-2k.txt](rewrite-2k.txt) | [rewrite-3k.txt](rewrite-3k.txt) |
11+
| Transition algebra | [algebra-1k.txt](algebra-1k.txt) | [algebra-2k.txt](algebra-2k.txt) | [algebra-3k.txt](algebra-3k.txt) |
12+
| Resumable evaluator pseudocode | [machine-1k.txt](machine-1k.txt) | [machine-2k.txt](machine-2k.txt) | [machine-3k.txt](machine-3k.txt) |
13+
14+
The byte counts and SHA-256 hashes are in [manifest.json](manifest.json).
15+
Run `bun docs/objective-bend/capsules/check.ts` from the repository root to
16+
check the exact published files and budgets.
17+
18+
The 1 KB files explicitly describe a projection: open recursion, lazy records,
19+
effects and the governed object boundary. They omit other constructors and
20+
protocol detail. The 2 KB files cover the complete reference core dynamics,
21+
including primitive edge cases, demand order, raw yields and resumption, with
22+
a small object contract. The 3 KB files add typing constraints and durable
23+
interaction rules. Their typing accounts have different emphases; none is a
24+
complete checker, admission algorithm, source grammar or wire specification.
25+
Budget is not a claim of completeness beyond each file's stated scope.
26+
27+
Start with the rewrite 3 KB capsule for the common agent contract. The machine
28+
2 KB capsule is the most direct implementation prompt. Algebra is useful for
29+
factoring demand rules and host admission into explicit mathematical relations.
30+
These preferences are editorial judgments, not results of a blind reconstruction
31+
test. The three authors consulted the implementation. The pseudocode has not
32+
been run as an interpreter, and prose inspection is not an equivalence proof.
33+
34+
## Why payer is outside the object tuple
35+
36+
The abstract object is `(id, code pin, versioned state, law)`; invocation adds
37+
an authenticated principal and authority. Host policy supplies execution and
38+
retention resources. The tuple is a semantic projection, not Mini's serialized
39+
record layout. Eliding storage accounting does not erase resource admission.
40+
41+
Mini's [ObjectRecord](../../../Kernel/ObjectRecord.lean) stores a Book account
42+
that funds the record and state cell. `admitWrite_payer_irrelevant` and
43+
`judge_payer_irrelevant` prove that changing that field does not change the
44+
corresponding write judgment. This supports omitting it from the common object
45+
description. It does not prove that changing funding preserves retention,
46+
execution availability or every observable host behavior.
47+
48+
The activity [escrow](../../../Kernel/ObjectiveActivity.lean) also has a field
49+
called `payer`, but that one is a `SubjectId` used as the activity principal in
50+
`factsOf`. The authenticated principal must remain. A Mini adapter still must
51+
populate all required accounts, signatures, reserves and principal fields;
52+
these capsules do not change that implementation.
53+
54+
## A small multi-language repository
55+
56+
A standalone conformance repository is a good destination. Keep one normative
57+
Lean semantics, independent implementations, a common corpus and named host
58+
profiles. Initially pin the relevant Mini source rather than fork its semantic
59+
authority. A proposed layout is:
60+
61+
```text
62+
capsules/ these compact contracts and exact byte manifests
63+
spec/ pinned Lean terms, reductions, yields, observations
64+
impl/python/ small transparent reference interpreter
65+
impl/c/ independent evaluator or imported existing backend
66+
conformance/ canonical AST, scripts of replies, fixtures, shrinking
67+
profiles/mini/ authority, durability, resource and transport binding
68+
examples/automatafl/ two-player game package, separate from language
69+
```
70+
71+
This is a proposed extraction, not a newly published repository. Mini already
72+
contains [a Lean C emitter](../../../Compiler/ObjectiveBendEmitC.lean),
73+
[a C demand machine](../../../native/objective-emit/runtime.c), and
74+
[a Python differential harness](../../../native/objective-emit/differential.py).
75+
The Python harness is not a Python language implementation. The C runtime is
76+
derived from the demand machine design and depends on Lean-generated ROM; it
77+
is useful existing work, not a blind independent reading of a capsule. Its
78+
emitter explicitly records that C refinement is not proved. No C qualification
79+
was rerun for this documentation experiment.
80+
81+
Use a canonical core AST first, so parser differences do not mask evaluator
82+
differences. Compare ground observations, stuck outcomes, yielded plans and
83+
resumed traces. Probe lazy records/closures by elimination rather than comparing
84+
their implementation representations. Separate finite fuel exhaustion from
85+
semantic divergence. Independent evaluators need not take the same number of
86+
steps; the existing C backend's stricter state/tick comparison remains useful
87+
for its particular claim of matching the Lean demand machine.
88+
89+
The next useful small swarm is a blind reconstruction experiment: one fresh
90+
agent per capsule, with only that capsule and a common AST/protocol interface,
91+
builds an interpreter. Score held-out behavioral cases and missing-rule reports,
92+
not just successful parses. Count the shared interface separately: it must not
93+
silently supply semantics omitted from the capsule. Include unused divergence,
94+
duplicate fields, strict AND, a wrong-typed left primitive operand followed by
95+
a yielding right operand, inherited methods seeing overridden self, and
96+
resumption under nested contexts. Test host profiles separately with stale
97+
roots, current law changes, duplicate resume and an unknown commit outcome.
98+
99+
The aim is the smallest contract from which independent agents reconstruct the
100+
same behavior. Byte golfing is only one part of that measurement.
101+
102+
## Source anchors
103+
104+
- [Core terms, values, Step, Yields](../../../Theory/ObjectiveBendOpenRecursion.lean)
105+
- [Types and usage safety](../../../Theory/ObjectiveBendTypes.lean)
106+
- [Modular typing](../MODULAR-TYPING.md)
107+
- [Durable interaction contract](../../OBJECTIVE-BEND-EVENTS.md)
108+
- [Two-player Automatafl comparison](../../../world/automatafl/README.md)
109+
110+
The original [capsule](../EMERGENT-SMALLTALK.txt) remains as a prose-oriented
111+
presentation; it now separates funding and fixes the typing/sharing shorthand.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Emergent Smalltalk / algebra kernel (partial profile).
2+
Scope: open recursion + governed objects; omits scalars, sums, reflection,
3+
typing, concrete syntax/wire and resource tariffs.
4+
Terms: x,\x.t,t u,mix(a,b),fix(e,p),{fs},t.k,extend(t,fs),done(t),perform(t).
5+
V=\x.t|{fs}; fields are unevaluated ordered string-key lists; first key wins.
6+
Capture-free substitution; fresh s,p; application left-associates.
7+
(\x.t)u -> t[u/x]
8+
mix(a,b) -> \s.\p.b s (a s p)
9+
fix(e,p) -> e (fix(e,p)) p
10+
{fs}.k -> fs[k]
11+
extend({fs},gs) -> {gs++filter(key notin keys(gs),fs)}
12+
done(t) -> t
13+
Close -> ONLY under E=[]|E t|E.k|extend(E,fs).
14+
E[perform(p)] yields (p,E); reply r resumes E[r]. Stuck != divergence.
15+
Object=(id,code pin,versioned state,law); ref != authority.
16+
A turn proposes writes; commit atomically iff exact read roots and current
17+
laws/authority and budget admit; otherwise no app writes. Calls co-commit; sends defer.
18+
Sponsorship/budget is host context, not object identity. No owner bypass.
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
Emergent Smalltalk / transition algebra.
2+
Scope: core dynamics + host skeleton; not typing, surface/wire syntax or tariffs.
3+
Application associates left; \x.t binds x; substitution avoids capture.
4+
fs/arms: ordered string-key lists, first match wins. N,B,S: disjoint Nat,
5+
Bool,String. Values v=\x.t|N|B|S|{fs}|spec(m,e)|proto(s,t)|inj(k,t);
6+
value components stay unevaluated. Fresh binders below.
7+
(\x.t)u -> t[u/x]
8+
mix(a,b) -> \s.\p.b s (a s p)
9+
fix(e,p) -> e (fix(e,p)) p
10+
spec(m,e)u -> e u
11+
reflect(proto(s,t)) -> s
12+
metadata(spec(m,e)) -> m
13+
target(proto(s,t)) -> t
14+
{fs}.k -> fs[k]
15+
extend({fs},gs) -> {gs++filter(key notin keys(gs),fs)}
16+
if(true,a,b) -> a; if(false,a,b) -> b
17+
natcase(0,z,x.b) -> z; natcase(n+1,z,x.b) -> b[n/x]
18+
case(inj(k,t),arms) -> b[t/x] if arms[k]=x.b
19+
done(t) -> t
20+
op(v,w) -> scalar result iff correctly typed:
21+
Nat +,*,-,/,%,=,<,<=; unbounded; -=truncated subtraction;
22+
/=floor; n/0=0; n%0=n. Bool AND (strict). String equality.
23+
-> is closed ONLY under
24+
E=[]|E t|E.k|extend(E,fs)|op(E,t)|op(v,E)|if(E,t,u)
25+
|natcase(E,z,x.b)|case(E,arms)|reflect(E)|metadata(E)|target(E).
26+
E[perform(p)] yields (p,E); response r resumes E[r].
27+
No step/yield/value=stuck; infinite ->=divergence. Pure -> has no effects.
28+
Every layer sees identical final self; super is the whole lower result.
29+
30+
Host: O=(id,code pin,versioned state,law). Ref != authority.
31+
Invoke pinned code on typed args/current view; effects propose a turn.
32+
Commit atomically iff exact read roots still match, every write passes current
33+
law/authority and host budget admits. Failure: no app writes; fees may apply.
34+
Sponsorship is host context, not object identity or authenticated principal.
35+
Calls co-commit; sends queue durable later turns with slots.
36+
Grants face current law; revision != revocation. No owner bypass.
37+
Slot=(one decider,deadline,one terminal outcome). Resume consumes a generation.
38+
No reply != failure: query receipt/retry SAME signed attempt. New identity
39+
may repeat effects. Private state requires protected reads.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
Emergent Smalltalk / demand-admission algebra.
2+
Scope: dynamics + typing/host sketch; not full checker/parser/wire/tariff.
3+
Object tuple abstracts kernel layout.
4+
Application left-associates; binders fresh; substitution capture-free.
5+
fs/arms: ordered string-key lists, first match wins. N,B,S: disjoint Nat,
6+
Bool,String. Values v=\x.t|N|B|S|{fs}|spec(m,e)|proto(s,t)|inj(k,t);
7+
Value components are unevaluated.
8+
(\x.t)u -> t[u/x]
9+
mix(a,b) -> \s.\p.b s (a s p)
10+
fix(e,p) -> e (fix(e,p)) p
11+
spec(m,e)u -> e u
12+
reflect(proto(s,t)) -> s
13+
metadata(spec(m,e)) -> m
14+
target(proto(s,t)) -> t
15+
{fs}.k -> fs[k]
16+
extend({fs},gs) -> {gs++filter(key notin keys(gs),fs)}
17+
if(true,a,b) -> a; if(false,a,b) -> b
18+
natcase(0,z,x.b) -> z; natcase(n+1,z,x.b) -> b[n/x]
19+
case(inj(k,t),arms) -> b[t/x] if arms[k]=x.b
20+
done(t) -> t
21+
op(v,w) -> scalar result iff correctly typed:
22+
Nat +,*,-,/,%,=,<,<=; unbounded; -=truncated subtraction;
23+
/=floor; n/0=0; n%0=n. Bool AND (strict). String equality.
24+
-> gives head contractions, NOT contextual equality.
25+
Demand D selects argument 1 of app,get,extend,if,natcase,case,reflect,metadata,
26+
target; op demands 1 if arg1 is not a value, else 2; absent otherwise.
27+
Only demanded child steps propagate; E nests demand positions.
28+
E[perform(p)] yields (p,E); response r resumes E[r].
29+
No step/yield/value=stuck; infinite ->=divergence. Pure -> has no effects.
30+
All layers share final self; super=whole lower result.
31+
32+
Host: O=(id,code pin,versioned state,law). Ref != authority.
33+
Invoke pinned code on typed args/current view; effects propose a turn.
34+
admit(t,W)=rootsMatch(t)&funded(t)&domains(W)&all(w in W,
35+
typed(w)&writerPin(w)&packageCreatorLawsNow(w)&authorityNow(w)).
36+
Commit iff admit: atomic app writes W, otherwise none; host fees may survive.
37+
Sponsorship is host context, separate from object identity and principal.
38+
Calls co-commit; sends queue durable later turns with answer slots.
39+
Grants face current law; revision != revocation. No owner bypass.
40+
Delegation is explicit and scoped to callee, method, code pin, arguments,
41+
uses/caps; callee law still applies. Nested frames inherit no principal
42+
merely by naming a reference.
43+
Slot=(one decider,deadline,one outcome: reply/refused/unknown/broken/expired). Resume consumes a generation.
44+
No reply != failure: query receipt; retry SAME signed attempt. New id may
45+
repeat effects. Private state requires protected reads.
46+
47+
Typing sketch: exact canonical rows; supports=width bounds; recursive records
48+
nominal. Closed fix:(C->V->C)->V->C. Open L=(Self bound B,Super bound C,
49+
output F); bounds rigid at declaration. Chain L1..Ln: choose final T, seed I0;
50+
sigma[k]={Self=T,Super=I[k-1]}; require T supports Bk[sigma[k]], I[k-1]
51+
supports Ck[sigma[k]]; I[k]=Fk[sigma[k]]; I[n]=T. Instantiate, then check
52+
whole closed output. Affine/linear safety uses <=1; reusable closures capture only
53+
unrestricted shareable values. No activities in shared fields/args/payloads.
54+
Exhaustion != success. -> is weak-head call-by-name; sharing requires
55+
representation adequacy.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import {readFileSync, readdirSync} from "node:fs";
2+
import {createHash} from "node:crypto";
3+
import {dirname, join} from "node:path";
4+
import {fileURLToPath} from "node:url";
5+
6+
const here = dirname(fileURLToPath(import.meta.url));
7+
const manifest = JSON.parse(readFileSync(join(here, "manifest.json"), "utf8"));
8+
const expected = ["algebra", "machine", "rewrite"].flatMap(style =>
9+
[1, 2, 3].map(size => `${style}-${size}k.txt`)).sort();
10+
const actual = readdirSync(here).filter(name => name.endsWith(".txt")).sort();
11+
if (JSON.stringify(actual) !== JSON.stringify(expected) ||
12+
JSON.stringify(Object.keys(manifest.capsules).sort()) !== JSON.stringify(expected))
13+
throw Error("Expected exactly three capsule families at three budgets");
14+
for (const name of expected) {
15+
const bytes = readFileSync(join(here, name));
16+
const limit = Number(name.match(/-([123])k\.txt$/)![1]) * 1000;
17+
const recorded = manifest.capsules[name];
18+
const sha256 = createHash("sha256").update(bytes).digest("hex");
19+
if (bytes.length >= limit || bytes.some(byte => byte > 127) ||
20+
bytes.length !== recorded.bytes || limit !== recorded.exclusiveLimit ||
21+
sha256 !== recorded.sha256)
22+
throw Error(`${name}: byte budget, ASCII or manifest mismatch`);
23+
console.log(`${name}: ${bytes.length} < ${limit} bytes; hash matches`);
24+
}
25+
console.log("Byte/identity checks only; semantic equivalence is not established here.");
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
OBJECTIVE BEND: coroutine sketch, not runnable code; core projection.
2+
W(t) returns weak-head values; all calls propagate yields, saving their stack.
3+
Substitution t[u/x] avoids capture; binders fresh; application left-associative.
4+
W(\x.t)=\x.t; W({fs})={fs}; scalar literals return themselves.
5+
W(f a): f=W(f); if f=\x.t return W(t[a/x]); else stuck.
6+
W(mix(a,b))=\s.\p.b s (a s p)
7+
W(fix(e,p))=W(e (fix(e,p)) p)
8+
W(t.k)=W(first(W(t),k))
9+
W(extend(t,gs))={gs++[f in W(t).fields | key(f) not in keys(gs)]}
10+
W(done(t))=W(t); W(perform(p))=W(yield(p))
11+
Records are ordered string-keyed lists. Missing/wrong shapes: stuck.
12+
One final self; super is the lower computation. Nothing else is forced. W may diverge.
13+
Host object=(id,code-pin,versioned-state,law). References confer no authority.
14+
Commit effects atomically iff read roots match and current law/authority admit.
15+
Sponsorship is host policy. Pure evaluation grants nothing.
16+
Omitted: other constructors/primitives, typing, messaging, wire, metering.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
OBJECTIVE BEND: coroutine pseudocode, not a tested interpreter.
2+
W(t) returns weak-head values. Nested calls propagate yields, retaining stack;
3+
yield(p) hands raw p to host; a supplied response becomes its return value.
4+
Capture-avoiding t[u/x]; fresh binders; left-associated application.
5+
Values: \x.t, Nat, Bool, String, {fs}, spec(m,e), proto(s,t), inj(k,t).
6+
W(v)=v: constructor children stay unevaluated; scalar types are disjoint.
7+
W(f a): match W(f): \x.t=>W(t[a/x]); spec(m,e)=>W(e a)
8+
W(mix(a,b))=\s.\p.b s (a s p)
9+
W(fix(e,p))=W(e (fix(e,p)) p)
10+
W(reflect(t)): proto(s,u)=W(t); return W(s)
11+
W(metadata(t)): spec(m,e)=W(t); return W(m)
12+
W(target(t)): proto(s,u)=W(t); return W(u)
13+
W(t.k): {fs}=W(t); return W(first(fs,k))
14+
W(extend(t,gs)): {fs}=W(t); return {gs++[f in fs | key(f) not in keys(gs)]}
15+
W(if(c,a,b)): match W(c): true=>W(a); false=>W(b)
16+
W(natcase(c,z,x.b)): match W(c): 0=>W(z); n+1=>W(b[n/x])
17+
W(case(c,arms)): inj(k,u)=W(c); x.b=first(arms,k); return W(b[u/x])
18+
W(done(t))=W(t); W(perform(p))=W(yield(p))
19+
W(op(a,b)): v=W(a); w=W(b); return primitive(op,v,w)
20+
Nat: +,*,-,/,%,=,<,<=; unbounded; subtraction saturates; floor division;
21+
n/0=0; n%0=n. Bool: strict AND. String: equality. Other operands: stuck.
22+
Fields/arms: ordered string-keyed lists; first match wins. Every missing
23+
case/field or shape mismatch is stuck. Infinite recursion diverges.
24+
Every layer shares final self s; p is the whole lower computation.
25+
Host: object=(id,code-pin,versioned-state,law). Reference != authority.
26+
An authenticated turn evaluates pinned code; effects commit atomically only
27+
if exact read roots match and current law/authority admit all writes.
28+
Pure computation grants no effects. Failed turns make no application writes.
29+
Sponsorship is host policy; caller principal remains semantically relevant.
30+
Sends return durable answer slots; delivery is a later turn. An absent reply
31+
is unknown: query the receipt and retry the identical request identity.
32+
Omitted: typing, full async protocol, wire, tariffs.

0 commit comments

Comments
 (0)