Collapse signal platform primitives into pure SX dicts

Replace _Signal class (Python) and SxSignal constructor (JS) with plain
dicts keyed by "__signal". Nine platform accessor functions become ~20
lines of pure SX in signals.sx. type-of returns "dict" for signals;
signal? is now a structural predicate (dict? + has-key?).

Net: -168 lines platform, +120 lines SX. Zero platform primitives for
reactivity — signals compile to any host via the bootstrappers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 00:04:38 +00:00
parent dcc73a68d5
commit f3a9f3ccc0
6 changed files with 121 additions and 169 deletions

View File

@@ -14,7 +14,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-13T22:49:51Z";
var SX_VERSION = "2026-03-13T23:56:11Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -54,13 +54,6 @@
}
Island.prototype._island = true;
function SxSignal(value) {
this.value = value;
this.subscribers = [];
this.deps = [];
}
SxSignal.prototype._signal = true;
function Macro(params, restParam, body, closure, name) {
this.params = params;
this.restParam = restParam;
@@ -115,7 +108,6 @@
if (x._lambda) return "lambda";
if (x._component) return "component";
if (x._island) return "island";
if (x._signal) return "signal";
if (x._spread) return "spread";
if (x._macro) return "macro";
if (x._raw) return "raw-html";
@@ -226,17 +218,6 @@
return new Island(name, params, hasChildren, body, merge(env));
}
// Signal platform
function makeSignal(value) { return new SxSignal(value); }
function isSignal(x) { return x != null && x._signal === true; }
function signalValue(s) { return s.value; }
function signalSetValue(s, v) { s.value = v; }
function signalSubscribers(s) { return s.subscribers.slice(); }
function signalAddSub(s, fn) { if (s.subscribers.indexOf(fn) < 0) s.subscribers.push(fn); }
function signalRemoveSub(s, fn) { var i = s.subscribers.indexOf(fn); if (i >= 0) s.subscribers.splice(i, 1); }
function signalDeps(s) { return s.deps.slice(); }
function signalSetDeps(s, deps) { s.deps = Array.isArray(deps) ? deps.slice() : []; }
// invoke — call any callable (native fn or SX lambda) with args.
// Transpiled code emits direct calls f(args) which fail on SX lambdas
// from runtime-evaluated island bodies. invoke dispatches correctly.
@@ -554,6 +535,9 @@
}
function mapDict(fn, d) { var r = {}; for (var k in d) r[k] = fn(k, d[k]); return r; }
// Predicate aliases used by transpiled code
var isDict = PRIMITIVES["dict?"];
// List primitives used directly by transpiled code
var len = PRIMITIVES["len"];
var first = PRIMITIVES["first"];
@@ -4244,6 +4228,33 @@ callExpr.push(dictGet(kwargs, k)); } }
// === Transpiled from signals (reactive signal runtime) ===
// make-signal
var makeSignal = function(value) { return {["__signal"]: true, ["value"]: value, ["subscribers"]: [], ["deps"]: []}; };
// signal?
var isSignal = function(x) { return (isSxTruthy(isDict(x)) && dictHas(x, "__signal")); };
// signal-value
var signalValue = function(s) { return get(s, "value"); };
// signal-set-value!
var signalSetValue = function(s, v) { return dictSet(s, "value", v); };
// signal-subscribers
var signalSubscribers = function(s) { return get(s, "subscribers"); };
// signal-add-sub!
var signalAddSub = function(s, f) { return (isSxTruthy(!isSxTruthy(contains(get(s, "subscribers"), f))) ? append_b(get(s, "subscribers"), f) : NIL); };
// signal-remove-sub!
var signalRemoveSub = function(s, f) { return dictSet(s, "subscribers", filter(function(sub) { return !isSxTruthy(isIdentical(sub, f)); }, get(s, "subscribers"))); };
// signal-deps
var signalDeps = function(s) { return get(s, "deps"); };
// signal-set-deps!
var signalSetDeps = function(s, deps) { return dictSet(s, "deps", deps); };
// signal
var signal = function(initialValue) { return makeSignal(initialValue); };