dfir_lang/graph/ops/
defer_tick.rs

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