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};