Named freeze scopes for serializable reactive state
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 3m20s

Replace raw CEK state serialization with named freeze scopes.
A freeze scope collects signals registered within it. On freeze,
signal values are serialized to SX. On thaw, values are restored.

- freeze-scope: scoped effect delimiter for signal collection
- freeze-signal: register a signal with a name in the current scope
- cek-freeze-scope / cek-thaw-scope: freeze/thaw by scope name
- freeze-to-sx / thaw-from-sx: full SX text round-trip
- cek-freeze-all / cek-thaw-all: batch operations

Also: register boolean?, symbol?, keyword? predicates in both
Python and JS platforms with proper var aliases.

Demo: counter + name input with Freeze/Thaw buttons.
Frozen SX: {:name "demo" :signals {:count 5 :name "world"}}

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-14 22:42:21 +00:00
parent a759f4da3b
commit cb4f4b85e5
4 changed files with 287 additions and 376 deletions

View File

@@ -14,7 +14,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-14T22:31:34Z";
var SX_VERSION = "2026-03-14T22:48:39Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -5292,73 +5292,72 @@ PRIMITIVES["eval-expr-cek"] = evalExprCek;
var trampolineCek = function(val) { return (isSxTruthy(isThunk(val)) ? evalExprCek(thunkExpr(val), thunkEnv(val)) : val); };
PRIMITIVES["trampoline-cek"] = trampolineCek;
// primitive-name
var primitiveName = function(f) { return (isSxTruthy(isLambda(f)) ? lambdaName(f) : (function() {
var result = NIL;
var names = ["+", "-", "*", "/", "=", "<", ">", "<=", ">=", "not", "and", "or", "str", "len", "first", "rest", "nth", "list", "cons", "append", "map", "filter", "reduce", "for-each", "some", "every?", "get", "keys", "dict", "dict?", "has-key?", "assoc", "empty?", "nil?", "number?", "string?", "list?", "type-of", "identity", "inc", "dec", "mod", "join", "split", "slice", "contains?", "starts-with?", "upper", "lower", "trim", "replace", "format"];
{ var _c = names; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; if (isSxTruthy((isSxTruthy(isNil(result)) && isSxTruthy(isPrimitive(name)) && isIdentical(f, getPrimitive(name))))) {
result = name;
} } }
return result;
})()); };
PRIMITIVES["primitive-name"] = primitiveName;
// freeze-registry
var freezeRegistry = {};
PRIMITIVES["freeze-registry"] = freezeRegistry;
// cek-serialize-value
var cekSerializeValue = function(val) { return (isSxTruthy(isNil(val)) ? NIL : (isSxTruthy(isNumber(val)) ? val : (isSxTruthy(isString(val)) ? val : (isSxTruthy(boolean_p(val)) ? val : (isSxTruthy(symbol_p(val)) ? val : (isSxTruthy(keyword_p(val)) ? val : (isSxTruthy(isList(val)) ? map(cekSerializeValue, val) : (isSxTruthy(isLambda(val)) ? [makeSymbol("lambda"), lambdaParams(val), lambdaBody(val)] : (isSxTruthy(isCallable(val)) ? [makeSymbol("primitive"), sxOr(primitiveName(val), "?")] : (isSxTruthy(isDict(val)) ? cekSerializeEnv(val) : (String(val)))))))))))); };
PRIMITIVES["cek-serialize-value"] = cekSerializeValue;
// cek-serialize-env
var cekSerializeEnv = function(env) { return (function() {
var result = {};
var ks = keys(env);
{ var _c = ks; for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; result[k] = cekSerializeValue(get(env, k)); } }
return result;
// freeze-signal
var freezeSignal = function(name, sig) { return (function() {
var scopeName = sxContext("sx-freeze-scope", NIL);
return (isSxTruthy(scopeName) ? (function() {
var entries = sxOr(get(freezeRegistry, scopeName), []);
entries.push({["name"]: name, ["signal"]: sig});
return dictSet(freezeRegistry, scopeName, entries);
})() : NIL);
})(); };
PRIMITIVES["cek-serialize-env"] = cekSerializeEnv;
PRIMITIVES["freeze-signal"] = freezeSignal;
// cek-serialize-frame
var cekSerializeFrame = function(frame) { return (function() {
var result = {};
var ks = keys(frame);
{ var _c = ks; for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; (function() {
var v = get(frame, k);
return dictSet(result, k, (isSxTruthy((k == "type")) ? v : (isSxTruthy((k == "tag")) ? v : (isSxTruthy((k == "f")) ? cekSerializeValue(v) : (isSxTruthy((k == "env")) ? cekSerializeEnv(v) : (isSxTruthy((k == "evaled")) ? map(cekSerializeValue, v) : (isSxTruthy((k == "remaining")) ? v : (isSxTruthy((k == "results")) ? map(cekSerializeValue, v) : (isSxTruthy((k == "raw-args")) ? v : (isSxTruthy((k == "current-item")) ? cekSerializeValue(v) : (isSxTruthy((k == "name")) ? v : (isSxTruthy((k == "update-fn")) ? cekSerializeValue(v) : (isSxTruthy((k == "first-render")) ? v : cekSerializeValue(v))))))))))))));
})(); } }
return result;
// freeze-scope
var freezeScope = function(name, bodyFn) { scopePush("sx-freeze-scope", name);
freezeRegistry[name] = [];
cekCall(bodyFn, NIL);
scopePop("sx-freeze-scope");
return NIL; };
PRIMITIVES["freeze-scope"] = freezeScope;
// cek-freeze-scope
var cekFreezeScope = function(name) { return (function() {
var entries = sxOr(get(freezeRegistry, name), []);
var signalsDict = {};
{ var _c = entries; for (var _i = 0; _i < _c.length; _i++) { var entry = _c[_i]; signalsDict[get(entry, "name")] = signalValue(get(entry, "signal")); } }
return {["name"]: name, ["signals"]: signalsDict};
})(); };
PRIMITIVES["cek-serialize-frame"] = cekSerializeFrame;
PRIMITIVES["cek-freeze-scope"] = cekFreezeScope;
// cek-freeze
var cekFreeze = function(state) { return {["phase"]: get(state, "phase"), ["control"]: get(state, "control"), ["value"]: cekSerializeValue(get(state, "value")), ["env"]: cekSerializeEnv(get(state, "env")), ["kont"]: map(cekSerializeFrame, get(state, "kont"))}; };
PRIMITIVES["cek-freeze"] = cekFreeze;
// cek-freeze-all
var cekFreezeAll = function() { return map(function(name) { return cekFreezeScope(name); }, keys(freezeRegistry)); };
PRIMITIVES["cek-freeze-all"] = cekFreezeAll;
// cek-thaw-value
var cekThawValue = function(val) { return (isSxTruthy(isNil(val)) ? NIL : (isSxTruthy(isNumber(val)) ? val : (isSxTruthy(isString(val)) ? val : (isSxTruthy(boolean_p(val)) ? val : (isSxTruthy(symbol_p(val)) ? val : (isSxTruthy(keyword_p(val)) ? val : (isSxTruthy((isSxTruthy(isList(val)) && isSxTruthy(!isSxTruthy(isEmpty(val))) && isSxTruthy(symbol_p(first(val))) && (symbolName(first(val)) == "primitive"))) ? getPrimitive(nth(val, 1)) : (isSxTruthy((isSxTruthy(isList(val)) && isSxTruthy(!isSxTruthy(isEmpty(val))) && isSxTruthy(symbol_p(first(val))) && (symbolName(first(val)) == "lambda"))) ? makeLambda(nth(val, 1), nth(val, 2), {}) : (isSxTruthy(isList(val)) ? map(cekThawValue, val) : (isSxTruthy(isDict(val)) ? cekThawEnv(val) : val)))))))))); };
PRIMITIVES["cek-thaw-value"] = cekThawValue;
// cek-thaw-env
var cekThawEnv = function(frozenEnv) { return (function() {
var result = makeEnv();
{ var _c = keys(frozenEnv); for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; envSet(result, k, cekThawValue(get(frozenEnv, k))); } }
return result;
// cek-thaw-scope
var cekThawScope = function(name, frozen) { return (function() {
var entries = sxOr(get(freezeRegistry, name), []);
var values = get(frozen, "signals");
return (isSxTruthy(values) ? forEach(function(entry) { return (function() {
var sigName = get(entry, "name");
var sig = get(entry, "signal");
var val = get(values, sigName);
return (isSxTruthy(!isSxTruthy(isNil(val))) ? reset_b(sig, val) : NIL);
})(); }, entries) : NIL);
})(); };
PRIMITIVES["cek-thaw-env"] = cekThawEnv;
PRIMITIVES["cek-thaw-scope"] = cekThawScope;
// cek-thaw-frame
var cekThawFrame = function(frozenFrame) { return (function() {
var result = {};
var ks = keys(frozenFrame);
{ var _c = ks; for (var _i = 0; _i < _c.length; _i++) { var k = _c[_i]; (function() {
var v = get(frozenFrame, k);
return dictSet(result, k, (isSxTruthy((k == "type")) ? v : (isSxTruthy((k == "tag")) ? v : (isSxTruthy((k == "f")) ? cekThawValue(v) : (isSxTruthy((k == "env")) ? cekThawEnv(v) : (isSxTruthy((k == "evaled")) ? map(cekThawValue, v) : (isSxTruthy((k == "remaining")) ? v : (isSxTruthy((k == "results")) ? map(cekThawValue, v) : (isSxTruthy((k == "raw-args")) ? v : (isSxTruthy((k == "current-item")) ? cekThawValue(v) : (isSxTruthy((k == "name")) ? v : (isSxTruthy((k == "update-fn")) ? cekThawValue(v) : (isSxTruthy((k == "first-render")) ? v : cekThawValue(v))))))))))))));
})(); } }
return result;
// cek-thaw-all
var cekThawAll = function(frozenList) { return forEach(function(frozen) { return cekThawScope(get(frozen, "name"), frozen); }, frozenList); };
PRIMITIVES["cek-thaw-all"] = cekThawAll;
// freeze-to-sx
var freezeToSx = function(name) { return sxSerialize(cekFreezeScope(name)); };
PRIMITIVES["freeze-to-sx"] = freezeToSx;
// thaw-from-sx
var thawFromSx = function(sxText) { return (function() {
var parsed = sxParse(sxText);
return (isSxTruthy(!isSxTruthy(isEmpty(parsed))) ? (function() {
var frozen = first(parsed);
return cekThawScope(get(frozen, "name"), frozen);
})() : NIL);
})(); };
PRIMITIVES["cek-thaw-frame"] = cekThawFrame;
// cek-thaw
var cekThaw = function(frozen) { return {["phase"]: get(frozen, "phase"), ["control"]: get(frozen, "control"), ["value"]: cekThawValue(get(frozen, "value")), ["env"]: cekThawEnv(get(frozen, "env")), ["kont"]: map(cekThawFrame, get(frozen, "kont"))}; };
PRIMITIVES["cek-thaw"] = cekThaw;
PRIMITIVES["thaw-from-sx"] = thawFromSx;
// === Transpiled from signals (reactive signal runtime) ===