I’ll make a stab at breaking this up as I know how you feel! First, here’s the code formatted for easier reading (use three ```ocaml
at start of the line and end with ``` on a newline).
(* Make Map of subtrees of t and their corresponding flow table locations *)
let flow_table_subtrees (layout : flow_layout) (t : t) : flow_subtrees =
let rec subtrees_for_table
(table : Field.t list)
(t : t)
(subtrees : t list) : t list
=
match FDD.unget t with
| Leaf _ -> subtrees
| Branch ((field, _), tru, fls) ->
if (List.mem table field) then
t :: subtrees
else
subtrees_for_table table tru subtrees
|> subtrees_for_table table fls
in
let meta_id = ref 0 in
List.map layout ~f:(fun table -> subtrees_for_table table t [])
|> List.filter ~f:(fun subtrees -> subtrees <> [])
|> List.foldi ~init:Map.Poly.empty ~f:(fun tbl_id accum subtrees ->
List.fold_right subtrees ~init:accum ~f:(fun t accum ->
Map.add accum ~key:t ~data:(tbl_id,(post meta_id))))
The first line (surrounded by (* *)
) is a comment. The next line
let flow_table_subtrees (layout : flow_layout) (t : t) : flow_subtrees =
is the start of the function definition of flow_table_subtrees
which has two parameters: layout
and t
. Unusually for OCaml the author has type annotated the parameters. So, (layout : flow_layout)
means that there is a parameter layout
that has type flow_layout
and (t : t)
means t
has type t
.
In addition, the return type of the function is defined (after the last :
) as flow_subtrees
. This level of type annotation is not common in OCaml as the compiler can almost always infer types.
Next the function subtrees_for_table
is defined to be recursive (the rec
keyword, by default OCaml functions are non-recursive).
let rec subtrees_for_table
(table : Field.t list)
(t : t)
(subtrees : t list) : t list
=
This function has 3 parameters: table
, t
and subtrees
and all have been type annotated so table
is a Field.t list
and so on. The return type is defined as a t list
. Note that as subtrees_for_table
is defined inside the function it will only be visible within this function.
Now we get to the body of subtrees_for_table
:
match FDD.unget t with
| Leaf _ -> subtrees
| Branch ((field, _), tru, fls) ->
This matches the return value from FDD.unget t
(the module FDD is defined outside of this code) with two possible variants: Leaf
and Branch
(ignoring the bits after the variant name). The variant type being match here has a declaration something like:
type tree = Leaf of ? | Branch of ((Field.t * ?) * (t * t)
The '?'s are not part of the language and just mean I don’t know the type. Checkout variant types for more detail.
In the case of Leaf
it has one parameter but we aren’t interested in the value so we use _
to ignore it (and stop the compiler compaining about unused values). When the return value of FDD.unget t
matches Leaf _
the value of the match statement becomes the value of the parameters subtrees
.
The case of Branch
is a little more complex. The Branch match
| Branch ((field, _), tru, fls) ->
matches the variant Branch
and the variables field
, tru
and fls
have their values set to the correspondence values of the variant. The body of the Branch
variant is then executed:
if (List.mem table field) then
t :: subtrees
else
subtrees_for_table table tru subtrees
|> subtrees_for_table table fls
What this says is: If field
is a member of the table
list then the value of the if is t
pre-pended to subtrees
and this will be the value of the match statement
The else case is a little more complex: The first step is to recursively execute subtrees_for_table table tru subtrees
which itself returns a t list
.
Before going on the ‘|>’ operator is worth looking at: let ( |> ) x f = f x
. so let f a = a + 1 in 5 |> f
will return 6. It gets more interesting with the partial application of functions. In the above subtrees_for_table table fls
is partially applied and is in fact a function that takes 1 argument, a t list
. Check out currying for more info.
So, now subtrees_for_table table tru subtrees |> subtrees_for_table table fls
actually means (subtrees_for_table table fls) (subtrees_for_table table tru subtrees)
, which is a t list
and is the value of the if…else and as a result the match statement.
In either case the value of the match becomes the value of the function subtrees_for_table
.
For the rest, check out lists in real world ocaml as well as the ref cell section in the imperative programming chapter.