macro_rules! __sliced__ {
($($tt:tt)*) => { ... };
}Expand description
Transforms a live collection with a computation relying on a slice of another live collection. This is useful for reading a snapshot of an asynchronously updated collection while processing another collection, such as joining a stream with the latest values from a singleton.
§Syntax
The sliced! macro takes in a closure-like syntax specifying the live collections to be sliced
and the body of the transformation. Each use statement indicates a live collection to be sliced,
along with a non-determinism explanation. Optionally, a style can be specified to control how the
live collection is sliced (e.g., atomically). All use statements must appear before the body.
let stream = sliced! {
let name1 = use(collection1, nondet!(/** explanation */));
let name2 = use::atomic(collection2, nondet!(/** explanation */));
// arbitrary statements can follow
let intermediate = name1.map(...);
intermediate.cross_singleton(name2)
};§Stateful Computations
The sliced! macro also supports stateful computations across iterations using let mut bindings
with use::state or use::state_null. These create cycles that persist values between iterations.
use::state(|l| initial): Creates a cycle with an initial value. The closure receives the slice location and returns the initial state for the first iteration.use::state_null::<Type>(): Creates a cycle that starts as null/empty on the first iteration.
The mutable binding can be reassigned in the body, and the final value will be passed to the next iteration.
let counter_stream = sliced! {
let batch = use(input_stream, nondet!(/** explanation */));
let mut counter = use::state(|l| l.singleton(q!(0)));
// Increment counter by the number of items in this batch
let new_count = counter.clone().zip(batch.count())
.map(q!(|(old, add)| old + add));
counter = new_count.clone();
new_count.into_stream()
};