Revive Halo as a scoped React action runtime - #17
Draft
robertdp wants to merge 16 commits into
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Revive Halo v4 as a modern, component-scoped action runtime for PureScript React.
This replaces the old Free/FreeAp evaluator with a direct ownership runtime while preserving the application's own monad. It adds safe component processes, typed managed tasks, subscriptions, synchronous cleanup, StrictMode reactivation, and comprehensive example-led documentation.
The v4 API is unreleased, so this PR favors one coherent public model over compatibility aliases or migration shims.
Public model
Halo augments an application monad rather than replacing it:
The interpreter is explicit at either React entry point:
Both expose current component state, an immutable task view, and synchronous action dispatch.
componentowns the complete React component;useHalocomposes with other hooks.Actions are handled through named lifecycle callbacks:
Application capabilities such as
MonadEffect,MonadAff,MonadAsk,MonadTell, andMonadThrowcontinue to route throughm. Halo ownsMonadStatefor component state and provides a direct abstractParallelcounterpart without Free/FreeAp.Ownership and concurrency
Every handler invocation is an independent root in one React activation. New roots capture the latest handlers, error callback, state setter, and application interpreter. Existing roots retain their interpreter snapshot.
forkcreates an independently cancellable component process:A fork may outlive its launching handler, but not its activation. It inherits the launching root's interpreter.
killremoves and fences it synchronously, then waits for Aff cancellation and finalizers.The runtime rejects stale state commits, capability registration, dispatch, and newly lifted application effects after kill or deactivation—even when code catches the initial Aff cancellation.
Typed managed tasks
React.Halo.Taskstores typed lifecycle outcomes in component state without introducing a task monad or retaining computations and inputs.For ordinary record state, one type-level label supplies both field location and identity:
Task.slotAt proxy lenssupports nested or custom lawful focuses.Task bodies remain ordinary
HaloMvalues returningEither error result:The policies are:
once— start only fromIdle; typed outcomes remain terminal until reset;startIfInactive— preserve active work, but restart from idle or terminal state;supersede— make the newest invocation authoritative immediately;debounce— trailing-edge latest-wins with a private cancellable timer; andreset— publishIdle, cancel active work, and await finalizers.Rendering observes tasks through the coherent runtime view:
Exact authority includes runtime identity, activation generation, and
ForkId. Ordinary component-state writes reconcile registered slots before publication. Copied, stale, cross-slot, or cross-runtime active values cannot commit, cross-cancel, or project as authoritative.Supersession fences old work before starting the replacement and does not wait for old finalizers. Reset fences and publishes immediately, then waits. Unexpected current task failures return the slot to
Idlebefore normalForkErrorrouting.Lifecycle, subscriptions, and cleanup
React development StrictMode setup-cleanup-setup creates a fresh usable activation. Deactivation fences the complete old activation before foreign cleanup or cancellation requests. If task state was normalized during cleanup, reactivation publishes the coherent normalized state before new
onActivatework.Halo replaces the Halogen subscription dependency with a small emitter API:
Emitterhas aFunctorinstance for mapping source values into actions without changing registration or cleanup behavior. Emitters deliberately do not impose queue, backpressure, or multi-source synchronization semantics.Subscriptions and generic cleanup are activation-scoped:
Deactivation attempts every remaining emitter and generic cleanup even if one throws. Cleanup failures route as
DeactivationErrorwithout blocking other cleanup or cancellation requests. Stale subscription callbacks and cleanup IDs cannot affect a later activation.React cleanup remains synchronous. Asynchronous resource release belongs in Aff finalizers owned by handlers, tasks, or forks; there is no asynchronous
onDeactivatecallback.Tooling and package cleanup
package.jsonlimited to the two development-tool dependencies.Documentation
componentanduseHalo.Validation
npm run format:checknpx spago build --strict --pedantic-packages --offline— zero project warnings or errors and no dependency findingsnpm test -- --offline— 41/41 passingnpx spago docs --offline--strict --pedantic-packages --offlinegit diff --checkIndependent bounded reviews covered the scoped runtime, AppM interpreter capture, forks, cancellation fencing, parallelism, task ownership, slot collisions, stale snapshots, StrictMode normalization, cleanup, subscriptions, errors, and documentation. The final branded task model was reviewed finding-free after its ownership regressions were added.
Known gaps
npm auditreports five vulnerabilities in the PureScript installer's development-only transitive dependencies. The forced remediation downgrades PureScript; Halo has no npm runtime dependencies.