In one line: the real job of a UI framework is deciding "when one value changes, what runs again?" GPUI's cx.notify() and signal-based components that track reads and "run once" give opposite answers to that question.

Why now

The problem

A UI is a function of state, and state changes a little at a time. So every UI framework carries some kind of incremental computation inside it. In his Xilem design post, Raph Levien quotes Ron Minsky: "hidden inside of every UI framework is some kind of incrementalization framework."

The questions to ask come down to two:

  1. When you write a value, what runs again? (the unit of re-execution)
  2. Who discovers that something changed? (the change detector)

The critique sums this up in one table:

Model What a write runs Who finds the change
Immediate (egui) All UI code, next frame Nobody
Elm (Iced) All of view, then a diff The diff
Views (GPUI) Every view without a cache You, with notify
Signals + re-run (Freya, Compose) Components that read it The framework
Signals + run once (Floem, Solid, zgui) Closures that read it The framework

The first three (immediate mode, Elm, views) are popular for good reasons. There's no state to synchronize, state lives in one place, render is plain Rust, and you need no macros or separate compiler. What the post asks is whether that simplicity holds up as the UI grows.

How it works

1. GPUI: notify means "something may have happened"

GPUI's README calls it "a hybrid immediate and retained mode" framework. State lives in an Entity (an Rc-like handle owned by GPUI), and the UI is rebuilt by Render::render each time. When you change state, you call cx.notify() yourself.

self.threads[i].unread = false;
cx.notify();

The key point is that Rust's &mut has no write barrier. The framework can't know which field changed, or whether anything changed at all. All notify carries is the entity id. As a result, every view outside a .cached() boundary gets re-rendered.

The critique compares this to Angular's zone.js. Angular's Signals RFC (Sub-RFC 1, April 2023) says zone.js "is only capable of notifying us when something might have happened in the application, and can give no information about what happened or what has changed," and concludes "This approach has not scaled well." notify is more precise than a zone, since it names an entity, but it makes the same kind of statement.