Source file fix_transform.ml

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
open! Core
open! Import
include Fix_transform_intf

module Make
  (Types : Types) (F : functor (_ : Recurse with module Types := Types) ->
    Transform with module Types := Types) : Transform with module Types := Types = struct
  module rec Recurse : (Recurse with module Types := Types) = struct
    let combine_up, empty, empty_for_lazy = Types.Up.(combine, empty, empty_for_lazy)

    open Trampoline.Let_syntax

    let default_c (type a) down acc (computation : a Computation.t)
      : (_ * _ * a Computation.t) Trampoline.t
      =
      match computation with
      | Return value ->
        let acc, up, value = User.transform_v down acc value in
        return (acc, up, Computation.Return value)
      | Leaf1 { model; input_id; dynamic_action; apply_action; input; reset } ->
        let acc, up, input = User.transform_v down acc input in
        return
          ( acc
          , up
          , Computation.Leaf1
              { model; input_id; dynamic_action; apply_action; input; reset } )
      | Leaf0 { model; static_action; apply_action; reset } ->
        return
          (acc, empty, Computation.Leaf0 { model; static_action; apply_action; reset })
      | Leaf_incr { input; compute } ->
        let acc, up, input = User.transform_v down acc input in
        return (acc, up, Computation.Leaf_incr { input; compute })
      | Sub { from; via; into; here } ->
        let%bind acc, up1, from = User.transform_c down acc from in
        let%bind acc, up2, into = User.transform_c down acc into in
        return (acc, combine_up up1 up2, Computation.Sub { from; via; into; here })
      | Store { id; value; inner } ->
        let acc, up1, value = User.transform_v down acc value in
        let%bind acc, up2, inner = User.transform_c down acc inner in
        return (acc, combine_up up1 up2, Computation.Store { id; value; inner })
      | Fetch { id; default; for_some } ->
        return (acc, empty, Computation.Fetch { id; default; for_some })
      | Assoc { map; key_comparator; key_id; cmp_id; data_id; by } ->
        let acc, up1, map = User.transform_v down acc map in
        let%bind acc, up2, by = User.transform_c down acc by in
        return
          ( acc
          , combine_up up1 up2
          , Computation.Assoc { map; key_comparator; key_id; cmp_id; data_id; by } )
      | Assoc_on t ->
        let acc, up1, map = User.transform_v down acc t.map in
        let%bind acc, up2, by = User.transform_c down acc t.by in
        return (acc, combine_up up1 up2, Computation.Assoc_on { t with map; by })
      | Assoc_simpl { map; by; may_contain_path } ->
        let acc, up, map = User.transform_v down acc map in
        return (acc, up, Computation.Assoc_simpl { map; by; may_contain_path })
      | Switch { match_; arms; here } ->
        let acc, up1, match_ = User.transform_v down acc match_ in
        let acc_and_upn_and_arms =
          arms
          |> Map.to_alist
          |> List.fold
               ~init:(return (acc, up1, []))
               ~f:(fun acc_and_up_and_arms (k, v) ->
                 let%bind acc, up, arms = acc_and_up_and_arms in
                 let%bind acc, up', v = User.transform_c down acc v in
                 return (acc, combine_up up up', (k, v) :: arms))
        in
        let%bind acc, upn, arms = acc_and_upn_and_arms in
        let arms = Map.of_alist_exn (module Int) arms in
        return (acc, upn, Computation.Switch { match_; arms; here })
      | Lazy t ->
        let t =
          Lazy.map t ~f:(fun t ->
            Trampoline.run
              (let%bind _acc, _up, t = User.transform_c down acc t in
               return t))
        in
        return (acc, empty_for_lazy, Computation.Lazy t)
      | Wrap
          { wrapper_model
          ; action_id
          ; result_id
          ; inject_id
          ; model_id
          ; inner
          ; dynamic_apply_action
          ; reset
          } ->
        let%bind acc, up, inner = User.transform_c down acc inner in
        let res =
          Computation.Wrap
            { wrapper_model
            ; action_id
            ; result_id
            ; inject_id
            ; model_id
            ; inner
            ; dynamic_apply_action
            ; reset
            }
        in
        return (acc, up, res)
      | With_model_resetter { inner; reset_id } ->
        let%bind acc, up, inner = User.transform_c down acc inner in
        return (acc, up, Computation.With_model_resetter { inner; reset_id })
      | Path -> return (acc, empty, Computation.Path)
      | Lifecycle value ->
        let acc, up, value = User.transform_v down acc value in
        return (acc, up, Computation.Lifecycle value)
    ;;

    let reduce_up l = List.reduce l ~f:combine_up |> Option.value ~default:empty

    let default_v (type a) down acc ({ value; id; here } : a Value.t) : _ * _ * a Value.t =
      let acc, up, value =
        match value with
        | Constant (c : a) -> acc, empty, Value.Constant c
        | Exception (e : exn) -> acc, empty, Exception e
        | Incr incr_node -> acc, empty, Incr incr_node
        | Named (name_source : Value.Name_source.t) -> acc, empty, Named name_source
        | Both (a, b) ->
          let acc, up_a, a = User.transform_v down acc a in
          let acc, up_b, b = User.transform_v down acc b in
          acc, combine_up up_a up_b, Both (a, b)
        | Cutoff t ->
          let acc, up, value = User.transform_v down acc t.t in
          ( acc
          , up
          , Cutoff
              { equal = t.equal; t = value; added_by_let_syntax = t.added_by_let_syntax }
          )
        | Map t ->
          let acc, up, value = User.transform_v down acc t.t in
          acc, up, Map { f = t.f; t = value }
        | Map2 t ->
          let acc, up1, t1 = User.transform_v down acc t.t1 in
          let acc, up2, t2 = User.transform_v down acc t.t2 in
          acc, reduce_up [ up1; up2 ], Map2 { f = t.f; t1; t2 }
        | Map3 t ->
          let acc, up1, t1 = User.transform_v down acc t.t1 in
          let acc, up2, t2 = User.transform_v down acc t.t2 in
          let acc, up3, t3 = User.transform_v down acc t.t3 in
          acc, reduce_up [ up1; up2; up3 ], Map3 { f = t.f; t1; t2; t3 }
        | Map4 t ->
          let acc, up1, t1 = User.transform_v down acc t.t1 in
          let acc, up2, t2 = User.transform_v down acc t.t2 in
          let acc, up3, t3 = User.transform_v down acc t.t3 in
          let acc, up4, t4 = User.transform_v down acc t.t4 in
          acc, reduce_up [ up1; up2; up3; up4 ], Map4 { f = t.f; t1; t2; t3; t4 }
        | Map5 t ->
          let acc, up1, t1 = User.transform_v down acc t.t1 in
          let acc, up2, t2 = User.transform_v down acc t.t2 in
          let acc, up3, t3 = User.transform_v down acc t.t3 in
          let acc, up4, t4 = User.transform_v down acc t.t4 in
          let acc, up5, t5 = User.transform_v down acc t.t5 in
          let up = reduce_up [ up1; up2; up3; up4; up5 ] in
          acc, up, Map5 { f = t.f; t1; t2; t3; t4; t5 }
        | Map6 t ->
          let acc, up1, t1 = User.transform_v down acc t.t1 in
          let acc, up2, t2 = User.transform_v down acc t.t2 in
          let acc, up3, t3 = User.transform_v down acc t.t3 in
          let acc, up4, t4 = User.transform_v down acc t.t4 in
          let acc, up5, t5 = User.transform_v down acc t.t5 in
          let acc, up6, t6 = User.transform_v down acc t.t6 in
          let up = reduce_up [ up1; up2; up3; up4; up5; up6 ] in
          acc, up, Map6 { f = t.f; t1; t2; t3; t4; t5; t6 }
        | Map7 t ->
          let acc, up1, t1 = User.transform_v down acc t.t1 in
          let acc, up2, t2 = User.transform_v down acc t.t2 in
          let acc, up3, t3 = User.transform_v down acc t.t3 in
          let acc, up4, t4 = User.transform_v down acc t.t4 in
          let acc, up5, t5 = User.transform_v down acc t.t5 in
          let acc, up6, t6 = User.transform_v down acc t.t6 in
          let acc, up7, t7 = User.transform_v down acc t.t7 in
          let up = reduce_up [ up1; up2; up3; up4; up5; up6; up7 ] in
          acc, up, Map7 { f = t.f; t1; t2; t3; t4; t5; t6; t7 }
      in
      acc, up, { value; here; id }
    ;;

    let on_value down acc behavior value =
      match behavior with
      | `Directly_on -> User.transform_v down acc value
      | `Skipping_over -> default_v down acc value
    ;;

    let on_computation down acc behavior computation =
      Trampoline.lazy_
        (lazy
          (match behavior with
           | `Directly_on -> User.transform_c down acc computation
           | `Skipping_over -> default_c down acc computation))
    ;;
  end

  and User : (Transform with module Types := Types) = F (Recurse)

  let transform_c down acc computation =
    Recurse.on_computation down acc `Directly_on computation
  ;;

  let transform_v down acc value = Recurse.on_value down acc `Directly_on value
end