Skip to content

Add refract: a store/lens-first reactive UI runtime experiment - #11

Closed
ealmloff wants to merge 1 commit into
mainfrom
devin/1786669930-refract-store-lens-runtime
Closed

ealmloff wants to merge 1 commit into
mainfrom
devin/1786669930-refract-store-lens-runtime

Conversation

@ealmloff

Copy link
Copy Markdown
Member

Summary

New workspace crate refract (publish = false): a from-scratch Dioxus-like reactive runtime built around stores and lenses, inspired by ealmloff/qk. See refract/DESIGN.md for the full rationale — especially the memo/resource design.

Core model:

  • Store<T> roots live in a thread-local generational slab; each value is a Box<RefCell<dyn Any>>, so guards are zero-copy Ref/RefMut projections (Ref::map) — no clones, no Rc<T> per field.
  • Lens/IndexLens compose over any Readable, carrying a structural path (Vec<u32>). Invalidation uses path-prefix overlap: writing store.field wakes readers of field and of the whole store, but not sibling fields; todos[3] doesn't wake todos[4].
  • Tracking via guards, qk RwTrack-style: ReadGuard::deref subscribes; WriteGuard::deref_mut flags the write, and notification happens on guard drop after releasing the RefMut. A WriteGuard that is only Deref'd never notifies.

Memo (Clean/Check/Dirty with version counters):

  • Lazy: writes push Dirty to direct subscribers and Check transitively; ensure() walks deps and only recomputes when a dep's version actually changed.
  • Equality-gated: version bumps only if the new value != old, so diamond graphs cut off.
  • Memos are brought up to date before handing out a ReadGuard, so recomputation never needs &mut while a lens guard into the memo is live. Cycles panic deterministically (computing flag), and holding a guard across a write to the same cell is a deterministic RefCell panic, never UB.

Resource (the Pin-sensitive part):

  • Value cell (ResourceState<T>: Pending/Ready/Reloading) and the driver Pin<Box<dyn Future<Output = ()>>> are stored separately — polling needs &mut on the future while lenses may hold Refs on the state, and the future may be self-referential after first poll so it is never moved out of its box.
  • Dep changes drop the old future (cancellation), preserve stale data as Reloading(T), and spawn a new one. Only reads in the synchronous source closure are tracked.

Also: eager Effects with ownership scopes (children/deps torn down on rerun, queue processed shallowest-first so parents dispose stale children), a minimal retained DOM (el/text/dyn_text/dyn_attr/dyn_children bound via effects, no VDOM), 15 integration tests, and counter/todos/weather examples.

Verification

  • cargo fmt --all -- --check
  • cargo clippy --workspace --all-targets --all-features -- -D warnings
  • cargo test --workspace --all-targets --all-features (15 refract integration tests) and cargo test -p refract --doc
  • RUSTDOCFLAGS=-Dwarnings cargo doc --no-deps --all-features -p refract
  • cargo run -p refract --example counter|todos|weather

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/edc42af2e89248a280abfa6508da3a43
Requested by: @ealmloff

Co-Authored-By: Staging-Devin AI <166158716+staging-devin-ai-integration[bot]@users.noreply.github.com>
@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant