Now for an advanced example of what one can do with this:
Unmarshalling Binary Data and Computing the Sum of an OCaml List
In this post, we’ll explore how to read binary marshalled data produced by an OCaml compiler and perform high-level list operations on it. Here’s a step-by-step explanation of the code:
First, we have a native OCaml program that creates a list of integers, marshals it to a string, and saves that string into aaa.marshal
:
let foo v =
let s = Marshal.to_string v [Compat_32] in
let ch = open_out_bin "aaa.marshal" in
let () = output_string ch s in
close_out ch
foo [1;2;3;4;5]
Next, we read the marshalled file, unmarshal it (using jsoo), and pass it to the sum function. The sum function operates at a high level on lists, but its definition uses the runtime representation that corresponds to OCaml’s runtime representation for lists:
let s = caml_read_file_content("./aaa.marshal")
@unboxed
type rec myList<'a> = | @as(0) Empty | Cons((unknown, int, myList<'a>))
let v: myList<int> = unmarshal(s)
let rec sum = l =>
switch l {
| Empty => 0
| Cons((_, i, l)) => i + sum(l)
}
Js.log2("v", v)
Js.log2("sum:", sum(v))
To see what the runtime representation looks like, the first log gives:
v [ 0, 1, [ 0, 2, [ 0, 3, [Array] ] ] ]
v
is a nested array where the first element is always 0, the second element is the integer, and the third element is the next item in the list.
The sum function walks the runtime representation directly and gives the correct sum 15
.
This approach demonstrates a neat technique for working with OCaml’s runtime representation to manipulate lists while maintaining high-level abstractions.