# New Signal Output - value - intersection - closure - dynamicClosure - destructureValue ## Single ```htmlembedded= <let/count = 0/> <div>${count}</div> ``` ```javascript= const _setup = (_scope) => { _count(_scope, 0); }; const _count = value("count", (_scope, count) => { _data(_scope["#text/0"], count); }); ``` ## Chain ```htmlembedded= <let/count = 0/> <const/doubleCount = count * 2/> <div>${doubleCount}</div> ``` ```javascript= const _setup = (_scope) => { _count(_scope, 0); }; const _count = value("count", (_scope, count) => { _doubleCount(_scope, count * 2); }); const _doubleCount = value("doubleCount", (_scope, doubleCount) => { _data(_scope["#text/0"], doubleCount); }); ``` ## Intersection ```htmlembedded= <let/count = 0/> <let/multiplier = 1/> <const/doubleCount = count * 2/> <const/multiCount = count * multiplier/> <div>${doubleCount} ${multiCount}</div> ``` ```javascript= const _setup = (_scope) => { _count(_scope, 0); _multiplier(_scope, 1); }; const _count = value("count", (_scope, count, dirty) => { if (dirty) { _doubleCount(_scope, count * 2); } _count·multiplier(_scope, dirty); }); const _multiplier = value("multiplier", (_scope, multiplier, dirty) => _count·multiplier(_scope, dirty) ); const _doubleCount = value("doubleCount", (_scope, doubleCount) => _data(_scope["#text/0"], doubleCount) ); const _multiCount = value("multiCount", (_scope, multiCount) => _data(_scope["#text/1"], multiCount) ); const _count·multiplier = intersection("count·multiplier", 2, (_scope) => _multiCount(_scope, _scope["count"] * _scope["multiplier"]) ); ``` ## Conditional + Closure ```htmlembedded= <let/count = 0/> <let/show = true/> <if=show> <const/doubleCount = count * 2/> <div>${doubleCount}</div> </if> ``` ```javascript= const _setup = (_scope) => { _count(_scope, 0); _show(_scope, true); }; const _count = _value("count", (_scope, count, _dirty) => { _count$ifBody(scope["ifScope"], count, _dirty); }); const _show = _value("show", (_scope, show, _dirty) => { setDynamicRenderer(_scope, _dirty, "#text/0", "ifScope", show ? _ifBody : null); }); // const _show = _value("show", (_scope, show, _dirty) => { // if (_dirty) { // setDynamicRenderer(_scope, "#text/0", "ifScope", show ? _ifBody : null); // } // callDynamicRendererClosures(_scope, "ifScope", _dirty); // }); const _ifBody = _register( "components/index.marko_1_renderer", _createRenderer( "<div> </div>", /* next(1), get */ "D ", null, [_count$ifBody] ) ); const _count$ifBody = _closure(1, "count", (_scope, count) => { _doubleCount$ifBody(_scope, count * 2) }); const _doubleCount$ifBody = _value("doubleCount", (_scope, doubleCount) => { _data(_scope["#text/0"], doubleCount) }); ``` ## Component + Closure ```htmlembedded= <let/count = 0/> <Component1> <const/doubleCount = count * 2/> <div>${doubleCount}</div> </Component1> ``` ```javascript= const _setup = (_scope) => { _count(_scope, 0); _Component(_scope["#childScope/0"]); _ComponentAttrs(_scope["#childScope/0"], { renderBody: bindRenderer(_scope, _Component1Body) }); }; const _count = _value("count", (_scope, count, _dirty) => { _dynamicSubscribers(_scope, "count"); }); const _Component1Body = _createRenderer( "<div> </div>", /* next(1), get */ "D ", null, [_count$Component1Body] ); const _count$Component1Body = _dynamicClosure(1, "count", (_scope, count) => { _doubleCount$Component1Body(_scope, count * 2) }); const _doubleCount$Component1Body = _value("doubleCount", (_scope, doubleCount) => { _data(_scope["#text/0"], doubleCount) }); ``` ## Loop + Closure ```htmlembedded= <let/count = 0/> <let/multipliers = [1,2,3]/> <for|multiplier| of=multipliers> <const/multiCount = count * multiplier/> <div>${multiCount}</div> </if> ``` ```javascript= const _setup = (_scope) => { _count(_scope, 0); _multipliers(_scope, [1,2,3]); }; const _count = _value("count", (_scope, count, _dirty) => { callLoopClosure(scope["forScopes"], _count$ifBody, count, _dirty); }); const _multipliers = _value("show", (_scope, multipliers, _dirty) => { setLoop(_scope, _dirty, "#text/0", "forScope", multipliers, _forBody, _forParams); }); // const _multipliers = _value("show", (_scope, multipliers, _dirty) => { // if (_dirty) { // setLoop(_scope, "#text/0", "forScope", multipliers, _forBody, _forParams); // } // callLoopClosures(_scope["forScopes"], _forBody, _forParams, _dirty); // }); const const _forBody = _register( "components/index.marko_1_renderer", _createRenderer( "<div> </div>", /* next(1), get */ "D ", null, [_count$ifBody] ) ); // TODO: figure out how to skip destructuring when dirty // since the value is undefined const _forParams = (_scope, [multiplier], _dirty) => { _multiplier$ifBody(_scope, multiplier, _dirty) } // _forParams = destructureValue(, // ([multiplier]) => [multiplier], // [_multiplier$ifBody] // ) const _count$ifBody = _closure(1, "count", (_scope, count, dirty) => { _count·multiplier(_scope, dirty) }); const _multiplier$ifBody = _value("multiplier", (_scope, multiplier) => { _count·multiplier(_scope, dirty) }); const _multiCount = value("multiCount", (_scope, multiCount) => _data(_scope["#text/0"], multiCount) ); const _count·multiplier = intersection("count·multiplier", 2, (_scope) => _multiCount(_scope, _scope._["count"] * _scope["multiplier"]) ); ``` # Runtime ```javascript= function value(valueAccessor, fn) { const markAccessor = valueAccessor + 1; const alwaysCall = fn.length === 3; return (scope, nextValue, dirty = true) => { let creation, currentMark; if (dirty === null) { currentMark = scope[markAccessor] = (scope[markAccessor] ?? 0) + 1; } else { creation = scope[markAccessor] === undefined; currentMark = scope[markAccessor] ||= 1; } if (currentMark === 1) { if (creation || alwaysCall || (dirty &&= scope[valueAccessor] !== nextValue)) { scope[valueAccessor] = nextValue; fn( scope, nextValue, dirty ); } } if (dirty !== null) { // closure needs this to be called after the fn // so it is marked until all downstream have been called scope[markAccessor]--; } }; } function intersection(dirtyAccessor, count, fn) { const markAccessor = dirtyAccessor + 1; const alwaysCall = fn.length === 2; return (scope, dirty) => { let creation, currentMark; if (dirty === null) { currentMark = scope[markAccessor] = (scope[markAccessor] ?? 0) + 1; } else { if (scope[markAccessor] === undefined) { scope[markAccessor] = count - 1; scope[dirtyAccessor] = true; } else { currentMark = scope[markAccessor]--; dirty = scope[dirtyAccessor] ||= dirty } } if (currentMark === 1) { if (dirty || alwaysCall) { scope[dirtyAccessor] = false; fn(scope, dirty); } } }; } function closure(dirtyAccessor, getOwnerValueAccessor, fn, ownerLevel, getOwnerScope = scope => scope._) { const markAccessor = dirtyAccessor + 1; const alwaysCall = fn.length === 2; getOwnerValueAccessor = typeof getOwnerValueAccessor === "function" ? getOwnerValueAccessor : () => getOwnerValueAccessor; return (scope, dirty = true) => { let ownerScope, ownerValueAccessor, currentMark; if (dirty === null) { currentMark = scope[markAccessor] = (scope[markAccessor] ?? 0) + 1; } else { if (scope[markAccessor] === undefined) { ownerScope = getOwnerScope(scope); ownerValueAccessor = getOwnerValueAccessor(); const ownerMark = ownerScope[ownerValueAccessor + 1]; const ownerHasRun = ownerMark === undefined ? !ownerScope.___client : ownerMark === 0; scope[markAccessor] = (currentMark = ownerHasRun ? 1 : 2) - 1; scope[dirtyAccessor] = true; } else { currentMark = scope[markAccessor]--; dirty = scope[dirtyAccessor] ||= dirty } } if (currentMark === 1) { if (dirty || alwaysCall) { scope[dirtyAccessor] = false; ownerScope ??= getOwnerScope(scope); ownerValueAccessor ??= getOwnerValueAccessor(); fn(scope, dirty && ownerScope[ownerValueAccessor], dirty); } } }; } function dynamicClosure(dirtyAccessor, getOwnerValueAccessor, fn, getOwnerScope = scope => scope._) { getOwnerValueAccessor = typeof getOwnerValueAccessor === "function" ? getOwnerValueAccessor : () => getOwnerValueAccessor; const signalFn = closure(dirtyAccessor, getOwnerValueAccessor, fn, getOwnerScope) return Object.assign( signalFn, { ___subscribe(scope) { const ownerScope = getOwnerScope(scope); const providerSubscriptionsAccessor = getOwnerValueAccessor(scope) + 2; ownerScope[providerSubscriptionsAccessor] ??= new Set(); ownerScope[providerSubscriptionsAccessor].add(bindSignal(scope, signalFn)); }, ___unsubscribe(scope) { const ownerScope = getOwnerScope(scope); const providerSubscriptionsAccessor = getOwnerValueAccessor(scope) + 2; ownerScope[providerSubscriptionsAccessor]?.delete( bindSignal(scope, signalFn) ); }, } ) } function contextClosure(valueAccessor, contextKey, fn) { const dirtyAccessor = valueAccessor - 2; return dynamicClosure( dirtyAccessor, (scope) => scope.___context[contextKey][1], value(valueAccessor, fn), (scope) => scope.___context[contextKey][0] ); } ``` # Considerations Can we go back to pure numerical offsets?