dfir_lang/graph/ops/
lattice_fold.rs

1use syn::parse_quote_spanned;
2
3use super::{
4    DelayType, OperatorCategory, OperatorConstraints, WriteContextArgs,
5    RANGE_0, RANGE_1,
6};
7
8/// > 1 input stream, 1 output stream
9///
10/// A specialized operator for merging lattices together into a accumulated value. Like [`fold()`](#fold)
11/// but specialized for lattice types. `lattice_fold(MyLattice::default)` is equivalent to `fold(MyLattice::default, dfir_rs::lattices::Merge::merge)`.
12///
13/// `lattice_fold` can also be provided with one generic lifetime persistence argument, either
14/// `'tick` or `'static`, to specify how data persists. With `'tick`, values will only be collected
15/// within the same tick. With `'static`, values will be remembered across ticks and will be
16/// aggregated with pairs arriving in later ticks. When not explicitly specified persistence
17/// defaults to `'tick`.
18///
19/// `lattice_fold` is differentiated from `lattice_reduce` in that `lattice_fold` can accumulate into a different type from its input.
20/// But it also means that the accumulating type must have a sensible default value
21///
22/// ```dfir
23/// use dfir_rs::lattices::set_union::SetUnionSingletonSet;
24/// use dfir_rs::lattices::set_union::SetUnionHashSet;
25///
26/// source_iter([SetUnionSingletonSet::new_from(7)])
27///     -> lattice_fold(SetUnionHashSet::<usize>::default)
28///     -> assert_eq([SetUnionHashSet::new_from([7])]);
29/// ```
30pub const LATTICE_FOLD: OperatorConstraints = OperatorConstraints {
31    name: "lattice_fold",
32    categories: &[OperatorCategory::LatticeFold],
33    hard_range_inn: RANGE_1,
34    soft_range_inn: RANGE_1,
35    hard_range_out: RANGE_1,
36    soft_range_out: RANGE_1,
37    num_args: 1,
38    persistence_args: &(0..=1),
39    type_args: RANGE_0,
40    is_external_input: false,
41    has_singleton_output: false,
42    flo_type: None,
43    ports_inn: None,
44    ports_out: None,
45    input_delaytype_fn: |_| Some(DelayType::MonotoneAccum),
46    write_fn: |wc @ &WriteContextArgs {
47                   root,
48                   is_pull,
49                   op_span,
50                   arguments,
51                   ..
52               },
53               diagnostics| {
54        assert!(is_pull);
55
56        let first_arg = &arguments[0];
57
58        let arguments = &parse_quote_spanned! {op_span=>
59            #first_arg,
60            |acc, item| { #root::lattices::Merge::<_>::merge(acc, item); }
61        };
62
63        let wc = WriteContextArgs {
64            arguments,
65            ..wc.clone()
66        };
67
68        // Can't do better type checking here because we need heavy type inference
69        // to support different accumulator and input types.
70        (super::fold::FOLD.write_fn)(&wc, diagnostics)
71    },
72};