Handling Nested Computations under Branching Logic
See original GitHub issueI hit an interesting problem that I’m gathering I’m just attacking wrong but I wanted to see what you think. This goes back to issues around trying to do conditionals with boolean statements that can run too many times without changing their results. Obviously breaking synchronicity is another option. But as you may remember I’d been creating Roots to handle disposal. I came across an issue there recently that I hadn’t noticed before, I’m gathering due to manual disposal getting queued up (ie it’s frozen like the rest).
Here is the simplest reproduction I have. You can understand why you wouldn’t expect data to be re-evaluated again after the ternary evaluates the other way. Thoughts?
https://codesandbox.io/s/objective-tdd-dif57
The issue is when the condition turned from true to false and value is set to undefined it still evaluates the nested child and fails to find a value with length.
EDIT I was wrong this even happens when you break synchronicity:
import S from "s-js";
S.root(() => {
const trigger = S.data(false),
data = S.data(null),
cache = S.value(S.sample(trigger)),
child = data => {
S(() => console.log("nested", data().length));
return "Hi";
};
S(() => cache(trigger()));
const memo = S(() => (cache() ? child(data) : undefined));
S(() => console.log("view", memo()));
S.freeze(() => {
trigger(true);
data("name");
});
S.freeze(() => {
trigger(false);
data(undefined);
});
});
This produces the same error.
EDIT 2 https://codesandbox.io/s/vigorous-cherry-vo79c shows an even simpler re-production using only a single condition and no freezing.
import S from "s-js";
S.root(() => {
const data = S.data(null),
cache = S.value(S.sample(() => !!data())),
child = data => {
S(() => console.log("nested", data().length));
return "Hi";
};
S(() => cache(!!data()));
const memo = S(() => (cache() ? child(data) : undefined));
S(() => console.log("view", memo()));
console.log("ON");
data("name");
console.log("OFF");
data(undefined);
});
Is this just by design and we should expect these states to exist at the same time?
Issue Analytics
- State:
- Created 4 years ago
- Comments:13 (2 by maintainers)
Top GitHub Comments
Hey Ryan, slow response, even for me 😦. Start-up ended up not working out, now on some intense contracting work trying to build back. Such is life.
On your examples up above, if I follow what you’re trying to do, the issue is that
cache
changes one tick afterdata
. Since the nested computation’s lifecycle followscache
, it runs one more tick than it should and sees theundefined
value indata
, causing the exception.Your code is probably boiled out of a more complicated actual case that would explain what you’re after, so this might not apply, but my thought would have been to do the simple thing:
I think the intent of
cache
and its setter was to be more performant (correct me if I have that wrong), but I think it likely went the other way. Fundamentally, something still needs to run every timevalue
changes. Adding the plumbing of a value signal and a computation that sets it doesn’t change that, it just adds more plumbing. So maybe tell me more about what you’re after and I can comment.I kept parity between subclocks and the main implementation for a while, but the stuff around lazy node construction broke that 😦. I’m mixed on whether lazy node construction is worth the complexity it added or not. At one point, it occurred to me that the initial case we were looking at – avoiding constructing unneeded computations in DOM components – would be largely eradicated just by making a single computation per component rather than per node, the way Surplus had been.
I created some benchmarks between double ended queues and the current, simpler Queue version, and preliminary it looks as if it does hit performance quite a bit.
If track cannot be implemented without causing too much slowdown I agree with @adamhaile that Subclocks probably is a better way forward as it solves more cases than track.