Take it any you want, as _signals_ or something else, what has been lacking in the JavaScript world and reimplemented many times is... functional reactive programming!
Our own take on this is https://github.com/okcontract/cells where we try to stick as close as possible to the spreadsheet analogy. We're using it to build a very complex web app and it's doing marvels.
`okcontract/cells` seems to be missing one of the two FRP primitives though: streams (of events).
One of the best FRP environment for JavaScript is/was FlapJax: https://en.m.wikipedia.org/wiki/Flapjax, which incorporates both the primitives and their operations.
It’s definitely not functional reactive programming, which needs to expose events and the history of events as first-order primitives, and usually a library of operations over the history of events.
If you simplify out the event stream primitive from FRP, what you are left with is not FRP.
Yes, as stated in the doc: "cells is a simplified Functional & Reactive (but not strictly FRP) library".
There were enough true FRP libraries around not to implement another one.
We think cells is a sweet spot where we get most of the benefits of functional programming and reactive programming while simplifying the concepts from the original paper from Elliott and Hudak.
Isn't this just an implementation of the Observable pattern? IIRC the difference between observables and signals is that signals don't recompute the value unless any of its sources change, by using a dependency graph. Also, .effect() is just what used to be called .subscribe() in knockout.js and others.
Properly implemented signals are more complex than the observer pattern, since they have to prevent duplicating updates if more than one dependency changes. They also fundamentally change the way you use them compared to observers, thanks to automatically subscribing to updates to their dependencies and propagating the updates to the dependents. Effects resembles observers, but you would generally avoid them when using signals unless necessary.
These are Observables, as seen in Svelte and RXJS. When talking about reactivity primitives / patterns, Observables and Signals are two of the most common / prominent ones. So in this context, they are closer to being 100% different things.
This seems a bit different from the kind of signals frameworks have, where dependencies are tracked automatically (no dependency array), and you can sort of chain stuff automatically, so for example you can have an effect that depends on 3 memos that depend on 4 signals or whatever else, and you never experience non-fresh values.
If you want to look a bit deeper into this I had written another sort of toy implementation that much more closely resembles what the frameworks are actually doing: https://github.com/fabiospampinato/flimsy
Nice! I recommend checking out mithril/stream [0], it's framework-agnostic and quite small but comes with your standard map/lift/etc. Does everything you need and with a standard, flexible interface.
I think the code itself is messy and outdated ES5, so I rewrote it in TypeScript and made some improvements to QOL, clarity and performance and use that package liberally across a lot of codebases. I'll post it here sometime whenever I push it to GitHub.
Ok, the EventTarget is a very cute trick and I haven't thought much about implementing signals this way.
However, all "serious" signal libraries have at least computed (higher order signals), and a way to sample a value, meaning get the value from a signal without triggering its effects, and no you can't just call foo.value because this won't work with computed signals, and a way to batch multiple write calls to multiple signals, which is often implemented by swapping the effects in and out of the update phase of the signal.
Also, you probably need a root to place signals and effects in a separate scope that can be isolated from the rest of the signal tree and disposed manually, for you know when you have a SPA with multiple pages and tables and you want to be able to update those tables and reorder them and go back and forward in your browser history without it leaking memory like the Titanic. I am struggling to do this myself so not saying I have all the answers here.
I really don’t see the point. All changes come from events whether user, system, or network events. All you need is event handlers, which is obviously already there at the lowest level.
The rest of the stuff the article mentions like rerendering and DOM diffing are all extremely inefficient framework nonsense that is entirely unnecessary.
So, to achieve the desired result be event oriented in everything you do, keep things simple, and don’t let framework nonsense distract from common sense.
It takes like 1 more example to actually have “signals” that automatically wire up dependencies between computed() and signal(). Without that, what’s the point?
Rich man’s signals provide a really interesting way to structure incremental computation, sometimes called “self-adjusting computation”, without manual book-keeping elbow-grease from the programmer. Ideally, one can use signals as containers for the inputs to your program and receive a program that automatically does the minimal possible re-computation when a signal changes.
Without that, “poor man’s signals” is just “stores” from React’s 2014 era.
I didn't know about that either. I find the new keyword useful as it instantly lets me know that I can expect the left-hand operator of the expression to be an object of a class, whereas cont foo = bar("value") doesn't say much about what foo is.
Firefox on Android