---
# System prepended metadata

title: New Signal Output

---

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