Skip to main content

Crate hyphae

Crate hyphae 

Source
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-swap for 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 map_query::MapQueryShareExt;
pub use map_query::SharedMapQuery;
pub use nested_map::NestedMap;
pub use pipeline::Definite;
pub use pipeline::Empty;
pub use pipeline::Materialize;
pub use pipeline::Pipeline;
pub use pipeline::PipelineShareExt;
pub use pipeline::Seedness;
pub use pipeline::SharedPipeline;
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 HashMap with per-key observability.
cell_set
Reactive HashSet with 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.