Open trusktr opened 5 years ago
Yeah, your second example is exactly what I'd recommend.
I tell people that there are two kinds of scenarios where we might want to set signals inside computations. One is event-based, the other is rule-based.
We can tell event-based ones, because if we describe what we want to do, our sentence usually starts with "when X changes ...." In your example above, we might describe it as "when word()
changes to "mary", increment number()
".
S.on(...)
is built to handle exactly these use cases, event-based logic. So here, your second take is spot on.
We can tell rule-based ones, because our sentence usually starts with "when X is ...." S()
is appropriate for rule-based scenarios, but we need to be careful that our predicate, the "X is" part, defines a condition for when the changes should stop.
For instance, your original code example could read "when word()
is "mary", number()
should be 1 more than number()
." That's an impossible rule to satisfy: there's no value of number()
that is one more than number()
. S starts incrementing number()
, looking for a solution, but never finds it, until the runaway clock watchdog kills the update.
On the other hand, this would work:
S(() => {
if (word() == 'mary' && number() < 4) {
number(number() + 1)
}
})
We could read this as "when word()
is "mary" and number()
is less than 4, number()
should be one more than number()
." (More idiomatically, we might say "number()
should start counting up.")
When this runs, S again starts incrementing number()
, but now it does find a solution, once number()
gets to 4.
Make sense?
In truth, there's a third case, more common than rule-based scenarios. That's where they've started writing a data signal with a setter computation, when their intent would be clearer with a simple computation, or even just a lambda. If the description isn't "when X is ..." but just "X is," then that's usually the case.
So say the description was "number()
is the length of word()
, and they'd written that as:
const number = S.data(0);
S(() => number(word().length));
That would be better and clearer as:
const number = S(() => word().length)
// or even just ...
const number = () => word().length
Here's a small recursive example:
It causes the first computation to run infinitely (until the error) because it reads and sets the
number
in the same computation.What's you advice for these sorts of situations in general?
In this case, maybe the hypothetical user meant:
Curious to know in what sorts of cases we might run into the infinite recursion, and how to deal with it.