Notes on let-signal evaluation
This commit is contained in:
parent
182307a81f
commit
c255e19c42
2 changed files with 53 additions and 89 deletions
|
|
@ -1,86 +0,0 @@
|
|||
|
||||
```javascript
|
||||
const Thing = (initState) => ({
|
||||
state: initState,
|
||||
subscribers: [],
|
||||
set(transform) {
|
||||
const state = transform(this.state);
|
||||
this.state = state;
|
||||
this.subscribers.forEach(f => {
|
||||
f(state);
|
||||
});
|
||||
},
|
||||
get() {
|
||||
return this.state;
|
||||
},
|
||||
subscribe(f) {
|
||||
this.subscribers.push(f);
|
||||
},
|
||||
map(transform) {
|
||||
const Y = Thing(this.state);
|
||||
this.subscribe(x => {
|
||||
Y.set(() => transform(x));
|
||||
});
|
||||
|
||||
return Y;
|
||||
},
|
||||
});
|
||||
|
||||
function pair(X, Y) {
|
||||
const Z = Thing([X.get(), Y.get()]);
|
||||
X.subscribe(x => {
|
||||
Z.set(() => [x, Y.get()]);
|
||||
});
|
||||
Y.subscribe(y => {
|
||||
Z.set(() => [X.get(), y]);
|
||||
});
|
||||
return Z;
|
||||
}
|
||||
|
||||
// Comonad lift
|
||||
// Signal(a), (Signal(a) -> b) -> Signal(b)
|
||||
|
||||
// X: Signal(A), f: Signal(A) -> B
|
||||
// Y: Signal(B)
|
||||
// function extend(X, f) {
|
||||
// const y0 = f(X);
|
||||
// const Y = Thing(y0);
|
||||
// X.subscribe(x => {
|
||||
// Y.set(f(???)); I need to somehow feed it a new signal...
|
||||
// // TODO: Ofcourse I can feed it `X` again, but that feels wrong... I thought
|
||||
// });
|
||||
// return Y;
|
||||
// }
|
||||
|
||||
const count = Thing(0);
|
||||
console.log("COUNT EXISTS");
|
||||
|
||||
console.log(count.get())
|
||||
|
||||
count.subscribe(x => {
|
||||
console.log("count is now", x);
|
||||
});
|
||||
|
||||
count.set(() => 1)
|
||||
count.set(() => 2)
|
||||
|
||||
const double = count.map(x => 2*x);
|
||||
console.log("DOUBLE EXISTS");
|
||||
|
||||
|
||||
double.subscribe(x => {
|
||||
console.log("double is now", x);
|
||||
});
|
||||
|
||||
count.set(() => 3);
|
||||
count.set(() => 9);
|
||||
|
||||
const WTF = pair(count, double)
|
||||
console.log("-> WTF EXISTS");
|
||||
|
||||
WTF.subscribe(([x, y]) => {
|
||||
console.log("WTF is now ", [x, y]);
|
||||
});
|
||||
|
||||
count.set(() => 13);
|
||||
```
|
||||
|
|
@ -472,9 +472,7 @@ let-signal {
|
|||
```
|
||||
|
||||
|
||||
|
||||
// === parametrised signals ===
|
||||
|
||||
# Parametrise Signals
|
||||
|
||||
```
|
||||
// like a top-level function of type (A, B, C) -> Signal(D)
|
||||
|
|
@ -483,3 +481,55 @@ fn-signal Foo(x1, x2, x3) {
|
|||
}
|
||||
```
|
||||
|
||||
# Implementation
|
||||
|
||||
## Signal Env/Frame/Binding
|
||||
|
||||
```
|
||||
type SignalFrame = {
|
||||
pattern: ProductPattern,
|
||||
expr: Signal<Value>,
|
||||
}
|
||||
```
|
||||
|
||||
This is almost like a signal-env. Seems useful.
|
||||
|
||||
## Let-Signal binding
|
||||
```
|
||||
let-signal {
|
||||
x := sig-expr-0,
|
||||
y := sig-expr-1
|
||||
. f(x, y)
|
||||
}
|
||||
```
|
||||
What happens during the evaluation of the above signal-expression?
|
||||
|
||||
1. evaluate `(sig-expr-0, sig-expr-1)` to `(sig0, sig1)` and construct a signal-env
|
||||
`[ x := sig0, y := sig1 ]`
|
||||
2. evaluate `initVal := f(x, y) in env [ x := sig0.read(), y := sig1.read() ]`
|
||||
3. construct new signal `Z := signal(initVal)`
|
||||
4. make `Z` depend on `sig0` and `sig1`.
|
||||
When one of them changes, push new value on `Z` that's the result of evaluation of
|
||||
`f(x, y) in env [ x := sig0.read(), y := sig1.read() ]`
|
||||
|
||||
Note how `Z` is a signal together with a special closure:
|
||||
- body of the closure is `f(x, y)`
|
||||
- the captured signal-env of the closure is `[ x := sig0, y := sig1 ]`
|
||||
- `Z` depends on `(sig0, sig1)`
|
||||
|
||||
|
||||
TODO: Maybe it would be better to have something like signal-values?
|
||||
These can either be plain constants,
|
||||
or something more complex that has dependencies...
|
||||
Right now everything is forced to be `Signal<Value>`.
|
||||
|
||||
```
|
||||
type Signal =
|
||||
| Constant(Value)
|
||||
| Closure(... ? ...)
|
||||
| NamedSignal(SignalName) // ???
|
||||
```
|
||||
|
||||
But... if we allow recompilation at runtime of signals, a constant signal may become something more complex with dependencies.
|
||||
That's why you always have to track dependencies - even when the original value ain't changing (atleast with the current compiled code)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue