[HN Gopher] Signals, the push-pull based algorithm
___________________________________________________________________
Signals, the push-pull based algorithm
Author : mpweiher
Score : 148 points
Date : 2026-04-04 08:31 UTC (2 days ago)
(HTM) web link (willybrauner.com)
(TXT) w3m dump (willybrauner.com)
| danieltanfh95 wrote:
| How is error handling expected to happen here?
| tcfhgj wrote:
| Result<T> should work
| ramon156 wrote:
| I love this pattern, but I always felt like squeezing idiom B
| into idiom A when doing this in e.g. TS. I never tried
| effect-ts, so maybe I'm missing out. Anyone have experience
| in this?
| nothinkjustai wrote:
| try-catch
| fabianholzer wrote:
| 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.
| willybrauner wrote:
| I searched for the "right way" to explain this algorithm, and
| ultimately parsing the code was the clearest for me. This isn't
| always true; sometimes a graph or an interactive module has
| more impact; it really depends on the topic. Thanks for the
| feedback.
| qprofyeh wrote:
| Kudos! Minor detail: the code could use a border or bg color
| different than the text, so that they're more distinct.
| noelwelsh wrote:
| 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.
| HumanOstrich wrote:
| After wondering what the heck glitch-freedom is and learning
| about it, I agree with you. It seems like it deserves at least
| a brief explanation in an article about how signals work.
|
| I've gone with the universal `alien-signals` package for my
| project (which doesn't use a frontend framework that includes
| signals). They show benchmarks of being by far the fastest and
| have strict limits on code complexity. Those limits are also
| supposed to avoid glitches by design, and now at least some of
| that is tested[1].
|
| [1]: https://github.com/stackblitz/alien-signals/pull/39
| willybrauner wrote:
| I didn't know about Flapjax, thanks I'll check it out. Glitch-
| freedom is indeed a gap in this article. I focused on the
| signal algorithm exclusively without some implementation
| optimisation like batching updates; there is so much more to
| cover! Maybe in a next one, Thanks!
| mpweiher wrote:
| And before there was "reactive", there were (oneway) dataflow
| constraints.
|
| And a lot of literature on the algorithms.
|
| I wrote a bit about the connection here:
|
| https://blog.metaobject.com/2014/03/the-siren-call-of-kvo-
| an...
|
| (It starts in a slightly different place, but gets there)
|
| Also about constraints as an architectural connector.
|
| https://dl.acm.org/doi/10.1145/2889443.2889456?cid=813164912.
| ..
| pygy_ wrote:
| Batching isn't just an optimisation, not having transactional
| semantics can cause bugs.
| mananaysiempre wrote:
| I wrote a whole screed here about how glitches are evil and Rx
| is evil for teaching people they're normal, but then I thought
| about it a bit more--
|
| The system as described isn't actually glitchy, is it? It
| doesn't eagerly run any user computations, just dirtying, and
| that is idempotent so the order is irrelevant. It's also a bit
| useless because it only allows you to pull out values of your
| own initiative, not subscribe to them, but that's fixable by
| notifying all subscribers after the dirtying is done, which
| can't cause glitches (unless the subscribers violate the rules
| of the game by triggering more signals).
|
| So now I'm confused whether all the fiddly priority-queue
| needlepoint is actually needed for anything but the ability to
| avoid recomputation when an intermediate node decides it
| doesn't want to change its output despite a change in one of
| its inputs. I remember the priority queue being one of the
| biggest performance killers in Sodium, so that can't be it,
| right?..
|
| I'm also confused about whether push-pull as TFA understands it
| has much to do with Conal Elliott's definition. I don't think
| it does? I feel like I need to reread the paper again.
|
| Also also, some mention of weak references would probably be
| warranted.
| mpweiher wrote:
| > whether push-pull as TFA understands it has much to do with
| Conal Elliott's definition.
|
| Virtually nothing that is getting sold/branded as "FRP" has
| anything to do with Conal Eliott's definition.
|
| I once gave a long talk about this here in Berlin, but I
| don't remember if there was a video.
|
| I've also explained it on twitter a bunch of times, including
| this memorable sequence:
|
| https://x.com/mpweiher/status/1353716926325915648
|
| Kinda like the Marshall McLuhan scene in Annie Hall ("if only
| real life were like this")
|
| https://www.youtube.com/watch?t=136&v=sXJ8tKRlW3E
| mananaysiempre wrote:
| >> whether push-pull as TFA understands it has much to do
| with Conal Elliott's definition.
|
| > Virtually nothing that is getting sold/branded as "FRP"
| has anything to do with Conal Eliott's definition.
|
| True but not what I meant. The article implicitly (and, in
| the links at the end, explicitly) refers to his 2009 paper
| "Push-pull functional reactive programming", which
| describes a semantic model together with an specific
| implementation strategy.
|
| So I was wondering if TFA's "push-pull" has anything to do
| with Elliott 2009's "push-pull". I don't think so, because
| I remember the latter doing wholly push-based recomputation
| of discrete reactive entities (Events and Reactives) and
| pull-based only for continuous entities that require
| eventual sampling (Behaviors).
|
| With that said, I find it difficult to squeeze an actual
| algorithm out of Elliott's high-level, semantics-oriented
| discussion, and usually realize that I misunderstood or
| misremembered something whenever I reread that paper (every
| few years). So if the author went all the way to reference
| this specific work out of all the FRP literature, I'm
| willing to believe that they are implying some sort of link
| that I'm not seeing. I would just like to know where it is.
| xkbear89 wrote:
| Glitch-freedom is one of those things that does not bite you
| until it does, and then you spend a day debugging a UI that
| renders an impossible intermediate state for a single frame. I
| hit this in a dashboard project where two derived signals
| depended on the same source, and without batched updates the
| downstream computation ran with one stale and one fresh value.
| The result was a brief negative number in a percentage display,
| which was only visible if you knew to look for it.
|
| The push-pull approach described here actually sidesteps the
| worst glitches because the dirty-flag propagation is just
| marking, not computing. But the article glosses over what
| happens during the pull phase when the dependency graph has
| diamonds. Topological sorting during pull is the standard fix
| -- Preact Signals and SolidJS both do this -- but it adds
| complexity that matters if you are rolling your own.
|
| Flapjax was doing a lot of this right in 2008. It is wild that
| the JS ecosystem took another 15 years to converge on
| essentially the same core ideas with better ergonomics.
| boxfire wrote:
| So yeah topological sorting is one element, but that global
| stack is a data race! You need to test set inclusion AND
| insert into it in an ordered way. Global mutex is gross. To
| do so lock-free could maybe be done with a lock free
| concurrent priority queue with a pair of monatomic generation
| counters for the priorities processed then next, then some
| memo of updates so that the conflicting re-update is
| invalidated by violation the generation constraint. I see no
| less than 3 CAS, so updates across a highly contentious
| system get fairly hairy. But still, a naive approach is good
| enough for the 99% so let there be glitches!
| aatd86 wrote:
| Can the reactive graph even be updated concurrently if the
| UI depends on it though? Because the UI is likely to run in
| its own single thread...
| Groxx wrote:
| yea, this is in javascript. it's inherently single-
| threaded in almost all contexts (e.g. node.js shared
| memory where you're intentionally bypassing core
| semantics for performance, and correctness is entirely on
| you)
| Groxx wrote:
| wouldn't this be solved by synchronously invalidating
| everything before computing anything? it seems like that's
| what the described system is doing tbh, since `setValue` does
| a depth-first traversal before returning. or is there a gap
| where that strategy fails you?
| cloogshicer wrote:
| What an amazing article. I really like the presentation of text
| scrolling together with the code. Wonder how this is done under
| the hood.
| willybrauner wrote:
| 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.
| cloogshicer wrote:
| Super cool, thanks for explaining! Is the code available
| somewhere?
| willybrauner wrote:
| No, unfortunately, my website is not open source. I may
| consider externalizing the blog in the future, while
| keeping all the internal article modules.
| rienbdj wrote:
| Sodium (and the book that goes with it) is a great resource too
| https://github.com/SodiumFRP/sodium
| cmacleod4 wrote:
| Nice presentation, looks like the same thing I implemented in Tcl
| here: https://wiki.tcl-lang.org/page/ReacTcl :-)
| Tade0 wrote:
| 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 wrote:
| Also in https://svelte.dev/blog/runes
| eviks wrote:
| 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)
| mockingloris wrote:
| 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
| willybrauner wrote:
| 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!
| tubs wrote:
| Why create an array each time it iterates a Set rather than just
| iterating the Set?
| willybrauner wrote:
| 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.
| willybrauner wrote:
| But as the article points out, this is a naive implementation.
| It could be discussed and greatly improved.
| ByteMe95 wrote:
| Relevant project for reactive stream programming that gets
| overlooked https://github.com/Point72/csp
| dunham wrote:
| 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...
___________________________________________________________________
(page generated 2026-04-06 23:01 UTC)