# 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?