-
Notifications
You must be signed in to change notification settings - Fork 54
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Do the benefits of signals as a language feature outweigh the costs of solidification into a standard? #220
Comments
Although it may have moved away from the term, Elm as a language had signals baked in from its inception. Clojure, rather than signals, adopted CSP ( But, like you, I share some apprehension about someone codifying their best idea of what makes a good signal library. RxJS and Bacon are popular but different. And RxJS is quite large. From my own experience, I have gotten by with signals with just a few primitives. I have not needed the sophisticated variations provided by RxJS. I am smitten with signal goodness, but I'm content to lean on standalone libraries (namely, my own), such is the variety in the implementations. |
@devmachiine @mlanza You might be surprised to hear that there is a lot of precedent elsewhere, even including the name "signal" (almost always as an analogy to physical electrical signals, if such a physical signal isn't being processed directly). I detailed an extremely incomplete list in #222 (comment), in an issue where I'm suggesting a small variation of the usual low-level idiom for signal/intereupt change detection. It's slow making its way to the Web, but if you squint a bit, this is only a minor variation of a model of reactivity informally used before even transistors were invented in 1926. Hardware description languages are likewise necessarily based on a similar model. And this kind of logic paradigm is everywhere in control-oriented applications, from robotics to space satellites. And almost out of necessity. Here's a simple behavioral model of an 8-bit single-operand adder-subtractor in Verilog, to show the similarity to hardware.And yes, this is fully synthesizable. // Adder-subtractor with 4 opcodes:
// 0 = no-op (no request)
// 1 = stored <= in
// 2 = out <= stored + in
// 3 = out <= stored - in
module adder_subtractor(clk, rst, op, in, out);
input clk, rst;
input[1:0] op;
input[7:0] in;
output reg[7:0] out;
reg[7:0] stored = 0;
always @ (posedge clk) begin
if (rst)
stored <= 0;
else case (op)
2'b00 : begin
// do nothing
end
2'b01 : begin
stored <= in;
end
2'b10 : begin
out <= stored + in;
end
2'b01 : begin
out <= stored - in;
end
endcase
end
endmodule Here's an idiomatic translation to JS signals, using methods instead of opcodes: class AdderSubtractor {
#stored = new Signal.State(0)
reset() {
this.#stored.set(0)
}
nop() {}
set(value) {
this.#stored.set(value & 0xFF)
}
add(value) {
return (this.stored.get() + value) & 0xFF
}
subtract(value) {
return (this.stored.get() - value) & 0xFF
}
} To allow for external monitoring in physical circuits, you'll need two pins:
Then, consuming circuits can detect the output's rising edge and handle it accordingly. This idiom is very common in hardware and embedded. And these aren't always one-to-one connections.Here's a few big ones that come to mind:
It's not as common as you might think inside a single integrated circuit, though, since you can usually achieve what you want through simple boolean logic and (optionally) an internal clock output pin. It's between circuits where it's most useful. Haskell's had similar for well over a decade as well, though (as a pure functional language) it obviously did signal composition differently: https://wiki.haskell.org/Functional_Reactive_Programming And keyboard/etc events are much easier to manage performantly in interactive OpenGL/WebGL-based stuff like simple games if you convert keyboard events to persistent boolean "is pressed" states, save mouse position updates to dedicated fields to then handle deltas next frame, and so on. In fact, this is a very common way to manage game state, and the popularity of just rendering every frame like this is also why Dear Imgui is so popular in native code-based games. For similar reasons, that library also has some traction in highly interactive, frequently-updating native apps that are still ultimately window- or box-based (like most web apps). If anything, the bigger question is why it took so long for front end JS to realize how to tweak this very mature signal/interrupt-based paradigm to suit their needs for more traditional web apps. |
As for other questions/concerns:
A single shared library isn't on its own a reason to do that. And sometimes, that library idiom isn't even the right way to go. Sometimes, it is truly one library, and the library has the best semantics: Sometimes, it's a few libraries dueling it out, like Moment and date-fns. The very heavy (stage 3) Sometimes, it's numerous libraries offering the same exact utility, like Object.keys = (o) ->
(k for own k, v in o)
Object.values = (o) ->
(v for own k, v in o)
Object.entries = (o) ->
([k, v] for own k, v in o)
Object.fromEntries = (entries) ->
o = {}
for [k, v] in entries
o[k] = v
o Speaking of CoffeeScript, that's even inspired additions of its own. And there's been many cases of that and/or other JS dialects inspiring additions.
There's also other cases (not just |
It's not that I don't think signals are a staple of development. They most certainly are, at least for me. I just think you might get some pushback on what makes sense for the primitives. JavaScript pipelines are also a necessity and what appeared as straightforward, because Babel had implemented F# pipelines long ago, ended up becoming so extremely fragmented in disagreement as to hit a standstill. And I see signals as far more sophisticated and varied than pipelines. For example, take this statement from the proposal:
This is one variety of how signals can be implemented. I don't like magic. When I was heavily into Ruby I grew to have a strong distaste for the amount of things which had to be understood as happening behind the curtain (e.g. magically). I came to prefer explicit. So there are a lot of ways signals might be implemented. And if the way the primitives are implemented is distasteful to a group of devs who have other priorities/philosophies for how it should be done, they'll end up using their preferred third-party library. And native signals library will become an extraneous cost. It gets loaded, like it or not, because it's part of the runtime. |
For context, I myself coming in was hesitant to even support the idea of signals until I saw this repo and dug deeper into the model to understand what was truly going on. And yes, there's been some pushback. In fact, I myself have been pushing back on two major components of the current design:
I also pushed back against I've also been pushing hard for the addition a secondary tracked (and writable) "is pending" state to make async function-based signals definable in userland. |
I don't know what the community sentiment is, but is there a library in JS land which is already considered the de facto standard? Would it be RxJS? If yes, wouldn't that library form the basis of this proposal? If no, doesn't the lack of a de facto standard seem to suggest a lack of consensus about what signals should be? I mean, why would this newish proposal/api more likely get it right over, say, a mature, well-known library? I am not arguing against the proposal. I appreciate signals and use them in most UIs I write. I just struggle to understand how a new proposal is is likely to get right what one of the existing libraries hasn't already gotten right. I recall there is a proposal for adding Observables to the runtime, which is directly related to signals. It's just that particular proposal offers a basic enough primitive/api to reach a consensus. If this one can likewise come up with the right primitives/api, great! I appreciate the effort. The one thing the Clojure team had going for it when it authored And since JS is baked into runtimes, whatever library is adopted becomes an irreversible choice. So this new library should be held as best of breed (by a community majority) after it's designed and used by those having tried the alternatives (RxJS, Bacon, xstream). If that happens, I guess it'd be ready for adopting into the core. I don't think it need be robust (e.g. all the signal varieties offered by RxJS). It just needs to define/showcase the right primitives/api. The robustness can be tacked on with optional libraries. The aim of getting native primitives is in the optimization, not the robustness. |
@mlanza Welcome to the world of the average new stage 1 proposal, where everything is wildly underspecified, somehow both hand-wavy and not, and extremely under flux. 🙃 https://tc39.es/process-document/ should give an idea what to expect at this stage. Stage "0" is the wildest dreams, and stage 1 is just the first attempt to bring a dose of reality into it. Stage 2 is where the rubber actually meets the road with most proposals. It's where the committee has solidified on a particular solution. Note that I'm not a TC39 member. I happen to be a former Mithril.js maintainer who's still somewhat active behind the scenes in that project. I have some specific interest in this as I've been investigating the model for a possible future version of Mithril.js. |
Good point, I agree. I can see the similarity between game designers and gamers who propose balances/changes which wouldn't benefit the game(rs) as a whole and/or have unintended consequences.
Interesting. Especially the circuitry example! Because X exists in Y isn't enough justification on its own to include X in Z. I don't think javascript is geared towards those use cases, it's more in the domain of c/zig.
Good point! I found the same to be true with the I reconsidered some of the pro's of signals being baked into the language(or DOM api) which I stated
There were similar concerns regarding the conclusion of not moving forward with the Observable proposal I think there will be a lot of repeat discussion:
I can appreciate the standardization of signals, but I'm not convinced that tc39 is the appropriate home for signals. The functionality can be provided via a library, which is much easier to extended and improve across contexts. |
@devmachiine You won't likely see But just as importantly, memory usage is a concern. If you have 50k signals in a complex monitoring app (say, 500 items, with 15 discrete visible text fields, 50 fields across 4 dropdowns, 20 error indicators, and 5 inputs), and you can shave off an average of about 20 bytes of each of those signals by simply removing a layer of indirection (2x4=8 bytes) and not allocating arrays for single-reference sets (2x32=64 bytes per impacted object, conservatively assumes about 20% are single-listener + single-parent), you could've shaved off around entire entire megabyte of memory usage. And that could be noticeable. |
I think its great that there is a drive towards standardization of signals, but that it is too specialized to be standardized here
(
please let us know in an issue
- that's what this issue is for)Signals is a interesting way to approach state management, but it is much more complicated of a concept compared to something like a PriorityQueue/Heap/BST etc, which I think would be more generally useful as part of javascript itself.
What problem domains besides some UI frameworks would benefit out of it? Are there examples of signals as part of a language feature in other programming languages ?
What would be the benefit of having signals baked-in as a language feature over having a library for doing it?
When something is part of a standard, it's more work & time involved to make changes/additions, than if it was a stand-alone library. For signals to be a part of javascript, I think there would have to be a big advantage over a library.
I can imagine some pros being
Benefits being part of javascript, the same being true if it is part of the DOM api instead
I can imagine some cons being
I think in the very least a prerequisite for this as a language feature should be that
almost all of the web frameworks
use a shared library for theircore model of Signals
, then it would be proven that there is a use-case for signals as a standard, and much easier to use that shared library as an API reference for a standard implementation.If anyone could please elaborate more on why signals should be a language feature instead of a library, this issue could serve as a reference for motivation to include it in javascript. 🙃
The text was updated successfully, but these errors were encountered: