[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)