Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some long-needed renaming and consolidation (#90)Co-authored-by: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> #90

Merged
merged 47 commits into from
Apr 14, 2023

Conversation

wycats
Copy link
Member

@wycats wycats commented Apr 13, 2023

This PR restructures the internals around the concepts that emerged from use over time, and cleans up and consolidates concepts that weren't paying their way.


The various "Internals" types have been renamed to "Tag" (the Glimmer concept, more or less, but with significantly more focus on avoiding the Glimmer footguns that came from free-floating tags disconnected from values).


Much more in the new tags README.


The library of reactive primitives was consolidated to focus on enumerating the fundamental primitives needed to build reactive abstractions. Cells and markers mostly got a coat of fresh paint, while Formula got a much more significant retooling. Previously, there was a lot of duplication and not-fully-fleshed out concepts (PolledFormula and the primitives to enable formulas that were manually started and finished, to support cases where a framework provides before/after lifecycle hooks but no way to wrap rendering itself).

Formulas were consolidated and fleshed out into:

  • FormulaLifecycle, which gives you a function to call when your computation is done. It then gives you a stable FinalizedFormula back which you can call update() on to repeat the process. FormulaLifecycle replaces all of the frame infrastructure, as well as the FormulaValidation infrastructure.
  • Formula, which takes a compute function and gives you a reactive value back. Like before, Formula is also a function (so you can refactor a normal function into a Formula and not have to change all of the callers to use .current). You can subscribe to a formula, but Formula replaces PolledFormula: it's not cached. Which brings us to...
  • CachedFormula, which behaves exactly like Formula, but will return the last value when reactive dependencies haven't changed.

This change in emphasis mostly comes from our experience implementing renderers that interface with existing reactivity systems. In the common case, we want to notify a framework that Starbeam dependencies have changed, but allow the framework's own reactive values to also invalidate the computation. For example, in React, we want users to be able to transparently use a useState value with Starbeam values, and have the component rerender correctly when either of them changes.

Subscription without caching fits the bill perfectly, and it ended up being a more sensible default in mixed-reactivity environments. Caching is still frequently very useful, and is appropriate in situations where you know that your formulas only rely on Starbeam reactivity.

It's possible that we may want to package the concepts differently in @starbeaam/universal, which is designed for universal code. The deciding factor on primitive naming was based on the fact that CachedFormula does more things than Formula, so it's clearer. That consideration shouldn't force our hand on programming model defaults, which we should revisit after landing this change to the primitives.


More in the new reactive README. I took a detour to flesh out the tags README, so it's not yet complete.


Finally, this PR starts to define the fundamental runtime requirements of the system. The current released version of Starbeam combines fundamental concepts with many, many conveniences in TIMELINE, and this PR sharply minimizes the runtime interface.

This change focuses in on the two primary runtime requirements:

  • interactions with cells (subscriptions)
  • interactions with autotracking (what the current codebase calls "frames")

Defining the interface carefully highlights how truly minimal it is, which enabled a lot of the cleanup and simplification around frames and formulas. Among other things, the entire concept of "frame" has been completely eliminated: it was really a facade for a few methods that helped us evolve the requirements, but which had started to become the tail wagging the dog.

The conveniences that frame used to provide are now part of the Formula primitives, which makes them easier to understand, but also useful in more situations.

A couple of things I think will come out of this work:

  1. I haven't yet moved the debugging interface into the runtime (other than the call stack function), but I think it'll be much easier to get debug code out of the way in production when it's modelled as part of the runtime (rather than as module state).
  2. I think the runtime definition is basically the answer to Full interop between multiple copies of Starbeam #87, and that getting a reliable solution to multiple Starbeams in the same process will just mean moving a piece of the implementation of the autotracking stack into @starbeam/shared. TBD, but I have a much clearer picture of the requirements now, and think it's now clear what we need to do.

This PR renames the timeline package to runtime, but I have not yet written a README for it.

wycats and others added 30 commits March 22, 2023 22:51
This commit takes a first stab at some holistic renaming of internal
concepts.

I'm going to take another stab at what this commit calls
"SubscriptionTarget" before submitting the PR (I think it's overly
narrow), but the overall cleanups here are good.

This commit also takes a step toward cleaning up and clarifying
the implementation of subscriptions. More to come.
Previously, the implementation of tags lived in `TIMELINE`, which has a
whole bunch of other unrelated stuff. So the abstraction went directly
from the interfaces defined in `@starbeam/interfaces` to the full-fledged
timeline definition in `@starbeam/timeline`.

This created an awkward situation where tags ought to have been real
objects (so they can share utility code), but we had utility function
instead that worked with the interfaces to avoid dragging all of
TIMELINE into the definition of tags.

This commit separates tags into their own package which clearly defines
the semantics of tags and provides concrete implementations for them.

This simplifies the code considerably, and also more clearly
communicates what tags are for.
Also consolidate and simplify the functions for interacting with them
And start establishing a minimal Runtime concept (related to #87)
Getting close to a new runtime foundation!
For some reason, the iterable on collections isn't invalidating.

The framework renderers also need to be updated.
The third attempt to reimplement resource on top of the new primitives worked
beautifully.

It supports `ResourceList`, but where the previous implementation worked
by implicitly adopting resources across runs, the new implementation of
`ResourceList` manages the lifetimes of its child resources explicitly.

The code is nearly as compact, in part because  the new resource
implementation is more honest about separating entire-resource state
from per-run state.

There's a bit of logic still left over supporting the adoption use-case,
which I will remove soon, but even with this leftover code, the new
resource implementation is still fairly compact.

Reworking resources is the last part of the current PR, and this commit
largely wraps up the design work on that.

The main remaining piece of work is updating the React and Preact
renderers, which should be straight forward.
This commit evolves the new resource API to support more signatures.
It also improves the types of the use function and use method to support
these improvements.

This PR also continues support for resource metadata (state that lives
for the entire lifetime of the resource and can be used for cross-run
state).

TL;DR use() takes an `IntoBlueprint` which is either a
`ResourceBlueprint` or a `ResourceConstructor`. In the next commit, it
will also take a function that returns a `ResourceBlueprint`, which will
bring back `use(() => Counter(start))`.

There are two remaining resource features needed for parity with the
system before the reactive primitive reform in this PR:

- Support for `use(() => Blueprint)
- Some way to provide an initial value to a resource, to support
  frameworks (React) that can't run the resource constructor until after
  the first render.

The second of these is tricky, because we want to avoid complicating
all resources just to help this one case. That said, now that we have
metadata that is provided to the constructor, perhaps you could provide
the initial value as a distinguished kind of metadata.
I'll have to think about it.
The first step towards GCing stale code after the refactor
@stackblitz
Copy link

stackblitz bot commented Apr 13, 2023

Review PR in StackBlitz Codeflow Run & review this pull request in StackBlitz Codeflow.

@wycats wycats changed the title Feature/primitive cleanup Some long-needed renaming and consolidation Apr 13, 2023
@wycats wycats mentioned this pull request Apr 13, 2023
@wycats wycats changed the title Some long-needed renaming and consolidation Some long-needed renaming and consolidation (#90)Co-authored-by: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Apr 14, 2023
@wycats wycats merged commit 7a33abe into main Apr 14, 2023
@wycats wycats deleted the feature/primitive-cleanup branch April 14, 2023 00:03
wycats added a commit that referenced this pull request Apr 14, 2023
…VoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>

This commit holistic renames, consolidates, and cleans up internal
concepts.

* Rename "internal" concepts to Tag and Tagged
* Simplify and improve the notion of subscriptions

## Created new packages

- @starbeam/runtime
- @starbeam/tags
- @starbeam/reactive
- @starbeam/resource
- @starbeam/service

### Created `@starbeam/tags`

Previously, the implementation of tags lived in `TIMELINE`, which has a
whole bunch of other unrelated stuff. So the abstraction went directly
from the interfaces defined in `@starbeam/interfaces` to the full-fledged
timeline definition in `@starbeam/timeline`.

This created an awkward situation where tags ought to have been real
objects (so they can share utility code), but we had utility function
instead that worked with the interfaces to avoid dragging all of
TIMELINE into the definition of tags.

This commit separates tags into their own package which clearly defines
the semantics of tags and provides concrete implementations for them.

This simplifies the code considerably, and also more clearly
communicates what tags are for.

## Notes from the implementation of resources

The third attempt to reimplement resource on top of the new primitives worked
beautifully.

It supports `ResourceList`, but where the previous implementation worked
by implicitly adopting resources across runs, the new implementation of
`ResourceList` manages the lifetimes of its child resources explicitly.

The code is nearly as compact, in part because the new resource
implementation is more honest about separating entire-resource state
from per-run state.

The new resource primitive adds support for resource metadata (state
that lives for the entire lifetime of the resource and can be used for
cross-run state).

---
Co-authored-by: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>
wycats added a commit that referenced this pull request Jun 13, 2023
…VoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>

This commit holistic renames, consolidates, and cleans up internal
concepts.

* Rename "internal" concepts to Tag and Tagged
* Simplify and improve the notion of subscriptions

- @starbeam/runtime
- @starbeam/tags
- @starbeam/reactive
- @starbeam/resource
- @starbeam/service

Previously, the implementation of tags lived in `TIMELINE`, which has a
whole bunch of other unrelated stuff. So the abstraction went directly
from the interfaces defined in `@starbeam/interfaces` to the full-fledged
timeline definition in `@starbeam/timeline`.

This created an awkward situation where tags ought to have been real
objects (so they can share utility code), but we had utility function
instead that worked with the interfaces to avoid dragging all of
TIMELINE into the definition of tags.

This commit separates tags into their own package which clearly defines
the semantics of tags and provides concrete implementations for them.

This simplifies the code considerably, and also more clearly
communicates what tags are for.

The third attempt to reimplement resource on top of the new primitives worked
beautifully.

It supports `ResourceList`, but where the previous implementation worked
by implicitly adopting resources across runs, the new implementation of
`ResourceList` manages the lifetimes of its child resources explicitly.

The code is nearly as compact, in part because the new resource
implementation is more honest about separating entire-resource state
from per-run state.

The new resource primitive adds support for resource metadata (state
that lives for the entire lifetime of the resource and can be used for
cross-run state).

---
Co-authored-by: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com>
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.

2 participants