The Missing Manual for Signals: State Management for Python Developers

76 buibuibui 33 6/13/2025, 11:55:01 AM bui.app ↗

Comments (33)

awkii · 13h ago
What the author touches on with before and after "declarative thinking" is largely applicable to all Directed Acyclic Graph (DAG) workflows, and not just signals. They are 100% correct that there is a mental shift. Yes, you can use magic to implicitly declare your DAGs with signals. You can also be really explicit with dependencies.

DAG-based workflows incur a cost in terms of complexity, but there are a few advantages to using DAGs instead of sequential.

1. Parallelism becomes inherently built-in

2. It's easier for a new developer to understand the direct dependencies of a node on other nodes (compared to sequential). Sometime in the future, a developer may want to split off a task or move it up/downstream of other tasks.

3. Fault tolerance & recovery becomes easier. Just because 1 step fails, doesn't mean that the whole workflow must come to a halt.

coldtea · 7h ago
What makes signals DAG?

User caution or does e.g. this lib prevents cycles?

jacques_chester · 13h ago
This triggered some associations for me.

Strongest was Cells[0], a library for Common Lisp CLOS. The earliest reference I can find is 2002[1], making it over 20 years old.

Second is incremental view maintenance systems like Feldera[2] or Materialize[3]. These use sophisticated theories (z-sets and differential dataflow) to apply efficient updates over sets of data, which generalizes the case of single variables.

The third thing I'm reminded of is Modelica[4], a language where variables are connected by relations (in the mathematical sense). So while A = B + C can update A on when B or C change, you also can update just A and B, then find out what C must have become.

[0] https://cells.common-lisp.dev

[1] https://web.archive.org/web/20021216222614/http://www.tilton...

[2] https://www.feldera.com

[3] https://materialize.com

[4] https://modelica.org

mananaysiempre · 11h ago
> Strongest was Cells[0], a library for Common Lisp CLOS. The earliest reference I can find is 2002[1], making it over 20 years old.

How about Microsoft DirectAnimation[1] from 1998, literally designed under the direction of Conal Elliott? Serious question, for what it’s worth, I’ve always wondered if all discussions of this thing are lost to time or if nobody cared for it to begin with.

[1] http://sistemas.afgcoahuila.gob.mx/software/Visual%20Basic%2...

PaulHoule · 11h ago
... or Visicalc, TK/Solver, etc.

I've always been baffled that people think spreadsheets are like dataframes when the really interesting thing has always been you can write formulas that refer to each other and the engine figures out the updating. Most of the times I've written a spreadsheet I haven't used the grid as a grid but just a place I can write some labels, some input fields and formulas.

cdavid · 9h ago
well it is both an easy way to compute in a dataframe context and a reactive programming paradigm. When combined, it gives a powerful paradigm for throwing data-driven UI, albeit non scalable (in terms of maintenance, etc.).
cdavid · 10h ago
One of the largest, if not the largest python codebase in the world, implements similar ideas to model financial instruments pricing: https://calpaterson.com/bank-python.html.
twic · 15h ago

  y = Computed(lambda: calculate_y(x()))
How does this instance of Computed know that it depends on x? Does it parse the bytecode for the lambda? Does it call the lambda and observe which signals get accessed?

In my homebrew signal framework, which emerged in the middle of a complicated web dashboard, this would look like:

  y = Computed([x], calculate_y)
So the machinery gets to see the signal directly.
buibuibui · 14h ago
I am using the standard Python library `contextvars.ContextVar` as the foundation of my reactivity system's dependency tracking mechanism. In the computation step, when Signals get accessed, I track them as dependencies.
TeeMassive · 7h ago
The module probably has its own global register and ever time Computed() is called it adds to it.
TOGoS · 13h ago
I've used systems that did this (some TypeScript TUI library comes to mind) and was similarly confused. I think what actually happened was that the x function/getter/whatever had some 'magic' in it that let it communicate with `Computed` as a side-effect of `Computed` computing the value.

Too magical for me. I'd rather have something like you described where inputs are explicit, so I don't have to guess about whether the magic will work in any given case.

pvillano · 8h ago
I have a dream for a compiled reactive DSL for video game programming that makes replay and rollback netcode automagically, eliminates bugs in state management, and naturally expresses derived state and the simulation step/transition function, while still being performant enough for real time

The performance hit from all that indirection of registering, getters, setters, discover, traversal, and lambdas could be avoided if we could compile the dag into smartly nested ifs

weiliddat · 12h ago
Hmm I have mixed feelings about this. I've thought about this topic for a bit, a couple of years ago I thought of bringing functional reactive programming to a backend node.js project (partially because of managing callback hell); in the past couple of years I work on an event/workflow system with 100+ million events per day.

This feels like a lighter weight alternative to Temporal or other workflow tools[0], but eventually for a backend system you'd likely be rebuilding features that are tailored for the backend.

In frontend code, you have many side effects (e.g. DOM, styling) that rely on a single piece of data/event, and more side effects that rely on those side effects (e.g. component hierarchy), and having this laid out declaratively is one way to understand the behavior of an application when this piece of data changes. You are also almost never concerned about durability/persistence of the state of data on the frontend, just because the code interacts with the browser and we almost never question the reliability of that API. A human is typically the "driver" of these interactions and is typically in the loop for most interactions, so stuff that fails, e.g. a failed network call, can bubble up to the user to deal with.

Conversely, web backend projects have code and infrastructure that are distributed (even monolithic ones), and most of the time are concerned with persisting state/data, distributing/scheduling workloads, etc. Each side effect / computation, especially ones that cross networks/service, has its own requirements for whether it should be at least once/at most once, retried/retry patterns, latency/throughput, failure modes/error handling. These requirements also define your boundaries/interfaces, rather than a nice semantic and declarative one (not exclusive but oftentimes the requirements win out).

Not saying that this signal-based approach can't be used in some areas of the application would benefit for declarative computation, but the examples given seem to indicate also a desire to do distributed workflow stuff.

[0] https://temporal.io/, https://github.com/meirwah/awesome-workflow-engines

rikafurude21 · 16h ago
I've been writing front-end javascript the "just use functions" way and never really wanted to get into React because it looks too complicated. But this makes sense. God damn it I want to actually learn react now.
jazzypants · 15h ago
This article never mentions React. This has nothing to do with React. There's a reason people say "react is not reactive"[1].

Signals are derived from Observables[2] which were first used in Adam Haile's S.JS[2] and made popular in JavaScriptLand by Ryan Carniato's SolidJS[3].

[1] - https://dev.to/this-is-learning/how-react-isn-t-reactive-and...

[2] - https://dev.to/this-is-learning/the-evolution-of-signals-in-...

[3] - https://www.solidjs.com/

[4] - https://github.com/adamhaile/S

Izkata · 8h ago
Since GP mentioned React, I think MobX was more popular there and seems to predate SolidJS. Also, Valtio is a modern version of the same idea but with a lot less boilerplate.
jauco · 12h ago
Fwiw knockoutjs seems to predate s.js (2010 vs 2013)

I can’t remember if at that point it was the first lib to uses observables.

jazzypants · 7h ago
Oof! I can actually still see a link to Knockout.js in my clipboard, so I clearly meant to add it as a source for that post. I also got the numbering all wrong, so I guess I should have spent another minute checking it for accuracy.

Thanks for the correction so that other people can learn!

WesolyKubeczek · 12h ago
> made popular in JavaScriptLand by Ryan Carniato's SolidJS[3]

Are you sure it hadn't been, by chance, made popular even before by KnockoutJS?

jazzypants · 7h ago
Yeah, as I said in response to another commenter, I actually had a link to Knockout in my clipboard. I'm pretty sure I was supposed to add the link after the word Observable.
troupo · 10h ago
Actual popularity to the point that they are now being adopted into the standard (not to say all frameworks except React) came thanks to Ryan. Though he explicitly acknowledges that original ideas come from Knockout, S and Marko
troupo · 16h ago
React is very far from signals (and very far from sane state management). Better alternatives:

- SolidJS (kickstarted the whole signals revolution)

- Svelte

- Preact (and Preact Signals)

- Well, even Angular got signals now

buibuibui · 14h ago
I actually created the library after being exposed to Angular Signals starting from the v16 release. I watched some talks and read articles about Signals, just to know about Ryan Carniato from SolidJS. He did an excellent job teaching the world about Signals!
lbreakjai · 14h ago
React doesn't really make many assumptions regarding state management. You're free to pick the library you want. Redux used to be the standard, but I worked on applications purely using RxJS, the way signals are presented in this article.
troupo · 10h ago
> You're free to pick the library you want.

They are all still pretty hampered by React's model: re-render (internally, in VDOM) the whole component on any minor change.

aquariusDue · 15h ago
There's also Datastar that uses signals and ends up being the best of HTMX and Alpine.js combined (at a smaller bundle size too).

https://data-star.dev/

yapyap · 15h ago
look into the Java observer pattern
HelloNurse · 13h ago
Two perplexing aspects:

- Why so many lambda functions instead of regular named functions? Is it a technical limitation? Something important should have a name, for instance (for the example in the article) different ways to compute greetings from names.

- How are the computations ordered, particularly multiple Effects that trigger at the same change? For instance, in the example in the article, when the name changes the updated greeting is printed before or after the updated location.

buibuibui · 11h ago
You can use normal names function instead of lambdas if you prefer! In Javascript anonymous functions are used normally for things, where defining named functions are considered too verbose - I use lambdas for that in Python.

The Signals evaluation are topologically ordered and are running synchronously. Effects are running in the order they are defined.

esafak · 16h ago
For bigger workflows, this declarative pattern is already implemented by orchestrators like Dagster, Flyte, and recently Airflow; e.g., https://dagster.io/blog/declarative-scheduling [fixed]
jpitz · 11h ago
Are you talking about Airflow Datasets and data-aware scheduling?

https://airflow.apache.org/docs/apache-airflow/2.4.0/release...

Isn't that about as recently as Dagster?

esafak · 6h ago
Yes, asset-aware scheduling.
ycombiredd · 16h ago
this 404's for me