Expand description
§hyphae - High-Performance Reactive Programming Library
A high-performance, type-safe concurrent reactive programming library with heterogeneous cell combinations and comprehensive dependency tracking.
§Features
- Fast Reads: Uses
arc-swapfor atomic value snapshots - Type-Safe: Full compile-time type checking with heterogeneous cell support
- Automatic Propagation: Changes flow through dependency chains automatically
- Dependency Tracking: Inspect and visualize cell relationships
- Thread-Safe: Safe concurrent access across threads
§Quick Start
use hyphae::{Cell, MapExt, Materialize, Mutable, Watchable, JoinExt, Pipeline, Signal, flat};
// Create reactive cells
let x = Cell::new(5).with_name("x");
let y = Cell::new(10).with_name("y");
// Pure operators (map/filter/...) return pipelines — no allocation
// until you materialize.
let doubled = x.clone().map(|val| val * 2).materialize().with_name("doubled");
// Combine multiple cells with join + flat!. join stays lazy, then creates
// its required fan-in coalescing boundary when the chain is materialized.
let sum = x.clone().join(y).map(flat!(|a, b| a + b)).materialize().with_name("sum");
// Subscribe on the materialized cell
let _guard = sum.subscribe(|signal| {
if let Signal::Value(value) = signal {
println!("Sum changed to: {}", value);
}
});
x.set(20); // Triggers updates§Pipelines vs Cells
Pure operators (map, filter, try_map, tap, map_ok, map_err,
catch_error, unwrap_or) return a Pipeline — an uncompiled chain
that has not yet been materialized into a Cell. Chaining pipelines
fuses closures at compile time; the fused closure runs only when a
consumer calls Materialize::materialize.
Cell is the materialized, cached, multicast form. Subscribing requires
a cell — there is no Pipeline::subscribe by design, forcing callers to
make the memoization decision explicit.
Definite pipelines materialize to Cell<T>; Empty pipelines such
as filter materialize to Cell<Option<T>> because they may not have an
honest initial value.
Operators that need state or multiple sources (debounce, buffer_*,
join, merge, switch_map, and others) are lazy pipelines too. Their
state and any required fan-in boundary are created only when the pipeline
is installed. Materialize at the point where a cached value, Gettable,
or Watchable boundary is required.
Derived collection views (CellMap::get, entries, items, keys,
size, len, diffs, and their CellSet counterparts) also expose
definite pipelines. Some reuse an internal cell today, making terminal
materialization a no-op, but callers cannot rely on that implementation
detail as an implicit observation boundary.
See the Hyphae 3.0 migration guide for owned-input and sharing examples.
§Combining Cells
Use join() to combine cells, and the flat! macro to avoid nested tuple destructuring.
join consumes its inputs into a lazy pipeline. It creates its required
fan-in coalescing boundary only when the chain is materialized:
use hyphae::{Cell, Gettable, JoinExt, MapExt, Materialize, flat};
let a = Cell::new(1);
let b = Cell::new(2);
let c = Cell::new(3);
let d = Cell::new(4);
// Without flat!: |(((a, b), c), d)| - deeply nested
// With flat!: |a, b, c, d| - clean and simple
let sum = a
.join(b)
.materialize()
.join(c)
.materialize()
.join(d)
.map(flat!(|a, b, c, d| a + b + c + d))
.materialize();
assert_eq!(sum.get(), 10);§Map Queries vs CellMaps
Pure CellMap operators return consuming, non-Clone MapQuery plans.
MapQuery exposes associated MapQuery::Key and MapQuery::Value
types. Semantic operators state their cardinality and key behavior:
select/select_by, map_values/filter_map_values,
map_entries/filter_map_entries, and flat_map_entries.
Plans compile to a statically typed, monomorphized runtime. Recognized
key-preserving and join regions fuse; rekeys and unsupported shapes remain
physical boundaries. No intermediate observable CellMap is created.
MapQuery::materialize is the sole observation boundary: it consumes the
plan, installs one subscription per interned physical root, and returns the
cached output map. Materialize once and clone that map to share work.
Named zero-sized ForeignKeyRelation markers give typed FK joins their
semantic relationship, partition, and index identity. Repeated uses of one
raw physical right source and relation share an index within a materialized
plan; transformed rights intentionally do not alias it.
Query closures must be deterministic, externally side-effect-free, and
nonblocking. They may run repeatedly or concurrently, and their invocation
count, order, and thread are not API guarantees. Output publication remains
deterministic, ordered, and synchronously settled. See map_query for
exact execution, collision, teardown, completion/error, and panic contracts.
Native builds with the scheduler feature may adaptively dispatch eligible
join-region work to Hyphae’s shared dedicated worker pool. Wasm and builds
without that feature execute map queries sequentially.
§CellMap Quick Start
use hyphae::{CellMap, MapQuery, traits::{InnerJoinExt, MapValuesExt}};
let users = CellMap::<String, &'static str>::new();
let scores = CellMap::<String, i32>::new();
users.insert("u1".into(), "alice");
scores.insert("u1".into(), 42);
// Chained operators return plan nodes — no intermediate CellMap
// until materialize().
let view = users
.clone()
.inner_join(scores.clone())
.map_values(|_, (name, score)| format!("{name}:{score}"))
.materialize();
assert!(view.contains_key(&"u1".to_string()));Re-exports§
pub use bounded_input::BoundedInput;pub use bounded_input::BoundedInputMetrics;pub use bounded_input::OverflowPolicy;pub use bounded_output::BoundedOutput;pub use cell::Cell;pub use cell::CellImmutable;pub use cell::CellMutable;pub use cell_map::CellMap;pub use cell_map::MapDiff;pub use cell_map::WeakCellMap;pub use cell_set::CellSet;pub use cell_set::SetDiff;pub use constructors::IntervalTick;pub use constructors::from_iter_with_delay;pub use constructors::interval;pub use constructors::interval_precise;pub use constructors::interval_precise_source;pub use constructors::interval_precise_with_elapsed;pub use constructors::interval_precise_with_elapsed_source;pub use constructors::interval_source;pub use map_query::MapQuery;pub use nested_map::NestedMap;pub use pipeline::Definite;pub use pipeline::Empty;pub use pipeline::Materialize;pub use pipeline::Pipeline;pub use pipeline::Seedness;pub use signal::Signal;pub use source::SampleOnSourceExt;pub use source::Source;pub use source::WeakSource;pub use subscription::SubscriptionGuard;pub use traits::AuditExt;pub use traits::BackpressureExt;pub use traits::BufferCountExt;pub use traits::BufferTimeExt;pub use traits::CatchErrorExt;pub use traits::CellValue;pub use traits::ColdExt;pub use traits::ConcatExt;pub use traits::CountByExt;pub use traits::DebounceExt;pub use traits::DedupedExt;pub use traits::DelayExt;pub use traits::DepNode;pub use traits::DistinctExt;pub use traits::DistinctUntilChangedByExt;pub use traits::FilterExt;pub use traits::FilterMapValuesPlan;pub use traits::FinalizeExt;pub use traits::FirstExt;pub use traits::FlatMapEntriesExt;pub use traits::ForeignKeyRelation;pub use traits::Gettable;pub use traits::GroupByExt;pub use traits::IdFor;pub use traits::IdType;pub use traits::InnerJoinExt;pub use traits::JoinExt;pub use traits::JoinKeyFrom;pub use traits::JoinedValuesPlan;pub use traits::KeyChange;pub use traits::LastExt;pub use traits::LeftJoinExt;pub use traits::LeftJoinPlan;pub use traits::LeftSemiJoinExt;pub use traits::MapEntriesExt;pub use traits::MapErrExt;pub use traits::MapExt;pub use traits::MapOkExt;pub use traits::MapValuesExt;pub use traits::MapValuesPlan;pub use traits::MergeExt;pub use traits::MergeMapExt;pub use traits::MultiLeftJoinExt;pub use traits::Mutable;pub use traits::OptionalRightKey;pub use traits::PairwiseExt;pub use traits::ParallelCell;pub use traits::ParallelExt;pub use traits::ProjectCellExt;pub use traits::ReactiveKeys;pub use traits::ReactiveMap;pub use traits::RequiredRightKey;pub use traits::RetryExt;pub use traits::RightJoinKey;pub use traits::SampleExt;pub use traits::ScanExt;pub use traits::SelectCellExt;pub use traits::SelectExt;pub use traits::SkipExt;pub use traits::SkipWhileExt;pub use traits::StateMachineBuilder;pub use traits::StateTransitionExt;pub use traits::SwitchMapExt;pub use traits::TakeExt;pub use traits::TakeUntilExt;pub use traits::TakeWhileExt;pub use traits::TapExt;pub use traits::ThrottleExt;pub use traits::TimeoutExt;pub use traits::TryMapExt;pub use traits::TwoLeftJoinMappedPlan;pub use traits::TwoLeftJoinPlan;pub use traits::UnwrapOrExt;pub use traits::Watchable;pub use traits::WatchableResult;pub use traits::WindowExt;pub use traits::WithLatestFromExt;pub use traits::ZipExt;pub use traits::join_vec;
Modules§
- bounded_
input - Bounded input channel for backpressure at system boundaries.
- bounded_
output - Bounded output channel for subscribers.
- cell
- cell_
map - Reactive
HashMapwith per-key observability. - cell_
set - Reactive
HashSetwith membership observability. - constructors
- flat
- map_
query - Uncompiled, statically typed reactive-map operation chains.
- nested_
map - Reactive grouped view over a
CellMap, indexed by foreign key. - pipeline
- Uncompiled reactive operation chains.
- signal
- source
Source<T>— a watchable event channel with no current value.- subscription
- traits
Macros§
- flat
- Macro to flatten nested tuple patterns from chained
join()calls.