Here throughout the program b is supposed to be the successor of a. Each time we reassign a, we need to explicitly mention b = a + 1.
Can we have a destiny operator as mentioned in this article that will bind a variable to its dependencies?
let age = 13;
const isEligible <= age > 18; // -> False
In the above above code, isElgible is bound to age > 18. Instead of storing a boolean, it keeps an expression in memory. Whenever we modify age and log isEligible, it should call the stored expression and return the result.
age = 20;
isEligible // -> True
Svelte uses a dollar label for this. It'd be nice to have something similar in JS. This will make variables reactive without needing to explicitly update them.
I can’t find a link but I have a vague memory of someone saying that this would go against a key invariant of JS - that accessed or writing to a variable in strict mode will never run arbitrary code.
Assuming accurate, while allowed for accessor properties as seen with claudepache's example, the original example would violate this given that isEligible is a variable and not a property.
There's a deferred part of decorators proposal that could help you with this: variable decorators.
You'd wrap the expression in a function, and the decorator would make every read call the function:
Here every time we call todosToDo for something like logging, it recalculates all the todos that are not done. This impacts the performance. Instead, we can bind todosToDo to todos so that it is only updated when todos is changed.
gets really tricky to implement in an engine. You're asking Javascript to watch a whole lot of unique values for changes, including:
The todos array itself (are new items added or removed?)
Array.prototype.filter (What if someone replaced the array's filter function on the prototype? We would need to re-evaluate this expression)
The done property of each todo in the array
In other words, this feature will create a lot of overhead, which could easily outweigh any potential optimizations.
I'll explain the "proper" way to do this kind of feature in a functional language, just so we can have another perspective on this problem.
Firstly, since I'm giving an example from othe functional paradigm, there won't be any variable mutations (as that's a side-effect, which is not a functional thing to do).
Next, we'll place our expression into a function, and explicitly pass any needed parameters to it
Finally, if we want to optimize things a bit, we would memoize the function, so that it remembers previous results. There's ways to fine toon a memoization algorithm to better fit the performance needs of the particular scenario.
Memoization isn't the only way to solve the type of issue you're describing, but it is one possible solution. In Javascript, it requires you to always keep your data structures immutable, so that you can safetly compare by identity (otherwise, you would have to use an expensive comparison algorithm to check if data has changed).
Seems to me like you're looking for a shortcut to do expensive processing.
function ToDo(desc) {
let done = false;
let listener = null;
return {
title: desc,
set listener(fn) {
listener = fn;
},
get done() { return done; },
set done(val) {
val = !!val;
if (val && val != done) {
done = val;
if (typeof(listener) == "function") {
listener(this);
}
}
}
}
}
function TodoList(...args) {
let list = [];
let done = [];
let retval = {
add(arg) {
if (arg.done) {
done.push(arg);
}
else {
arg.listener = listener;
list.push(arg);
}
},
get todos() { return list.slice(); },
get done() { return done.slice(); },
get all() { return this.todos.concat(this.done); }
};
function listener(obj) {
list.filter(item => obj != item);
if (!done.includes(obj)) {
done.push(obj);
}
}
for (let arg of args) {
retval.add(arg);
}
return retval;
}
This is just what it looks like to do your example somewhat efficiently. Trying to make something like this both generically and efficiently is just not suitable for a language like ES.
I'm new to functional programming, so if I'm right when todosToDo is referenced, it checks if any of its dependencies have changed (Here the todos array). If not, it takes the value from the cache. If yes it recalculates the expression.
I think there is a library called reselect that does this.
I've come to think of it and guess a dependant keyword would be better.
let a = 0;
dependant b = a + 3;
What I proposed is a bit different. In the above code, b is dependant on a. So when a is modified, if it has dependants, it updates them.
dependant console.log(`Hello ${a}`);
Above console.log is dependant on a. So whenever a is modified it gets called.
I'm not sure if this is the right way to do this, but thank you .
I respond there. In general I think the idea is great, but I believe it should have FRP dependency-tracking reactivity semantics, and avoid those of React, like the examples I added to that thread. It'll be more natural and concise that way I think.
@iliasbhal As a simple getter this could work for expression dependencies, just like a getter on any object, but it wouldn't be reactive, still 100% pull based. Then it would require memoization, unless the spec for this syntax has a special rule allowing JS engines to re-calculate values only on first use if it is "dirty". I think an if-dirty memoization feature could work. Hmm
For a dependency-tracking reactivity system, I think we need some way to opt into it syntactically, otherwise we need to provide a non-syntactical API much like libs like S.js and Solid.js do because if we overloaded regular JavaScript syntax to re-run then it means semantics of JS itself would be modified, which is impossible without breaking everyone.
get variables could be a syntactical way to opt into having "signals", assuming we can have at least a non-syntactical API given to us by the JS engine for being able to re-run expressions when their values change (like S() in S.js, or createEffect in Solid.js).