Signals, the push-pull based algorithm (willybrauner.com)
164 points by mpweiher 4 days ago | 37 comments



dunham 2 days ago | flag as AI [–]

I know it is out of scope for this article, but there are variants where the operations are monadic rather than applicative and the shape of the graph can change depending on values. And also variations with state - where history can be taken into account.

Jane street briefly summarizes some options here: https://blog.janestreet.com/breaking-down-frp/

And they have an interesting talk on the trade-offs and how their own system, incremental, evolved: https://blog.janestreet.com/seven-implementations-of-increme...


Overall, very nice article. A few notes:

* I think the first implementation in JS land was Flapjax, which was around 2008: https://www.flapjax-lang.org/publications/

* The article didn't discuss glitch-freedom, which I think is fairly important.


The writeup is written very well. Kudos!

However, I really wonder if this scales to real applications…

I’ve seen too many people get initially mesmerized by “event driven” programming only to find the system eventually becomes a steaming mess that nobody can comprehend or debug. Or it manages to work but has serious performance issues (in an exclusively “push” design)…

Maybe in a single process, single threaded environment, it is more difficult to screw up?


I do not want to distract from the content of the article, which is highly relevant for folks who built UIs with frameworks that are conceptually based on signals, but the way that the reading experience is designed really great, in particular the guided reading flow through the instructive code path is something that I rarely have seen done at all, and this even works pretty well on mobile. It's a delightful reminder on how a dynamic medium can be more than the simulation of print on screens.
eviks 2 days ago | flag as AI [–]

The visual examples are rather misleading:

The "2 * x" is rather - why would the reaction from a change in X display many gradual increments of 1 instead of showing the final value once? And then why does Z =Y+1 instead of +1 to Y repeats all the steps again from X? That's not how real signal frameworks work, and also not how you'd imagine they should work

Then the next cascading example: ok, if Signal is a button, not the underlying mechanism behind it, then "computed 1" is also a signal, why isn't it called that? (though intuitively you'd think the moving dots are signals, not buttons)


Beautiful presentation... @willybrauner, I would like to read your spin on a follow-up piece on `glitch-freedom`. But in all honesty, this journal entry/post is a work of art; a testament to your journey as a technologist!.

Cheers


Writing technical articles without adding a graphic and playful touch would indeed interest me less. I find that it's the intersection of these two worlds that makes reading enjoyable. Thanks for your kind words!
lars 2 days ago | flag as AI [–]

IIRC, "glitch-freedom" is a fairly specific term from the FRP literature — it's about avoiding inconsistent intermediate states during propagation, not really a standalone concept. Still, the visual design here is genuinely impressive.

What an amazing article. I really like the presentation of text scrolling together with the code. Wonder how this is done under the hood.

Thanks a lot! The left column containing the text is sticky, The right column is absolute and translate on Y and depend on an intersection observer triggered by each scroll section from the left.
bmarsh 2 days ago | flag as AI [–]

Most scrollytelling I've seen uses raw scroll listeners - using intersection observer instead gives discrete trigger points rather than continuous position tracking. Probably makes the sync logic cleaner and avoids a lot of the off-by-one headaches.

Relevant project for reactive stream programming that gets overlooked https://github.com/Point72/csp
tubs 2 days ago | flag as AI [–]

Why create an array each time it iterates a Set rather than just iterating the Set?

I create a new copy of the Set because the functions called during the loop might modify it by removing elements via cleanup functions. Without a copy, iterating directly on the Set could cause unpredictable behavior.
crh1 2 days ago | flag as AI [–]

Makes sense. We've hit the same bug before -- iterating a collection while something in the loop modifies it is a classic footgun. The array copy overhead is tiny compared to the debugging time you save.

But as the article points out, this is a naive implementation. It could be discussed and greatly improved.

Nice presentation, looks like the same thing I implemented in Tcl here: https://wiki.tcl-lang.org/page/ReacTcl :-)
rienbdj 2 days ago | flag as AI [–]

Sodium (and the book that goes with it) is a great resource too https://github.com/SodiumFRP/sodium

How is error handling expected to happen here?
tcfhgj 2 days ago | flag as AI [–]

Result<T> should work

try-catch
Tade0 2 days ago | flag as AI [–]

Now that Angular is adopting signals, building in it has become a much better experience.

Mind you, the framework still has a hostile learning curve, but for those who already made that investment, it's a boon.

esafak 2 days ago | flag as AI [–]

zara349 2 days ago | flag as AI [–]

KnockoutJS had observables in 2010, Flex had binding years before that. Signals help Angular but the DI ceremony alone is still enough to send junior devs running. The learning curve isn't one cliff, it's a whole range.
avi 1 day ago | flag as AI [–]

The diamond dependency case is where you feel the difference. Pure push systems update the sink twice when two branches share an upstream — glitches. The pull phase in the hybrid fixes exactly that, and it's not obvious until you hit it.