1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
use super::{
DelayType, OperatorCategory, OperatorConstraints, IDENTITY_WRITE_FN, RANGE_0,
RANGE_1,
};
/// Buffers all input items and releases them in the next tick.
/// the state of the current tick. For example,
/// See the [book discussion of Hydroflow time](../concepts/life_and_times) for details on ticks.
/// A tick may be divided into multiple [strata](../concepts/stratification); see the [`next_stratum()`](#next_stratum)
/// operator.
///
/// `defer_tick` is sometimes needed to separate conflicting data across time,
/// in order to preserve invariants. Consider the following example, which implements
/// a flip-flop -- the invariant is that it emit one of true or false in a given tick
/// (but never both!)
///
/// ```rustbook
/// pub fn main() {
/// let mut df = dfir_rs::dfir_syntax! {
/// source_iter(vec!(true))
/// -> state;
/// state = union()
/// -> assert(|x| if context.current_tick().0 % 2 == 0 { *x == true } else { *x == false })
/// -> map(|x| !x)
/// -> defer_tick()
/// -> state;
/// };
/// for i in 1..100 {
/// println!("tick {}", i);
/// df.run_tick();
/// }
/// }
/// ```
///
/// `defer_tick` can also be handy for comparing stream content across ticks.
/// In the example below `defer_tick()` is used alongside `difference()` to
/// filter out any items that arrive from `inp` in the current tick which match
/// an item from `inp` in the previous
/// tick.
/// ```rustbook
/// // Outputs 1 2 3 4 5 6 (on separate lines).
/// let (input_send, input_recv) = dfir_rs::util::unbounded_channel::<usize>();
/// let mut flow = dfir_rs::dfir_syntax! {
/// inp = source_stream(input_recv) -> tee();
/// inp -> [pos]diff;
/// inp -> defer_tick() -> [neg]diff;
/// diff = difference() -> for_each(|x| println!("{}", x));
/// };
///
/// for x in [1, 2, 3, 4] {
/// input_send.send(x).unwrap();
/// }
/// flow.run_tick();
///
/// for x in [3, 4, 5, 6] {
/// input_send.send(x).unwrap();
/// }
/// flow.run_tick();
/// ```
///
/// You can also supply a type parameter `defer_tick::<MyType>()` to specify what items flow
/// through the the pipeline. This can be useful for helping the compiler infer types.
pub const DEFER_TICK: OperatorConstraints = OperatorConstraints {
name: "defer_tick",
categories: &[OperatorCategory::Control],
hard_range_inn: RANGE_1,
soft_range_inn: RANGE_1,
hard_range_out: RANGE_1,
soft_range_out: RANGE_1,
num_args: 0,
persistence_args: RANGE_0,
type_args: &(0..=1),
is_external_input: false,
has_singleton_output: false,
flo_type: None,
ports_inn: None,
ports_out: None,
input_delaytype_fn: |_| Some(DelayType::Tick),
write_fn: IDENTITY_WRITE_FN,
};