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
use super::{
DelayType, OperatorCategory, OperatorConstraints, IDENTITY_WRITE_FN, RANGE_0,
RANGE_1,
};
/// Buffers all input items and releases them in the next tick.
/// See the [book discussion on time](../concepts/life_and_times) for details on ticks.
///
/// `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,
};