Binding Recursive Classes

You will often run into cases in which you need to bind classes that are cyclical. Here's an example:

class Foo:
    @staticmethod
    def make_bar():
        return Bar()

class Bar:
    @staticmethod
    def make_foo():
        return Foo()

Foo has a method that returns a Bar object, and Bar has a method that returns a Foo object.

While this works fine in Python, we have to be more explicit in OCaml in these kinds of situations.

Auto-generate bindings

As of version 0.4.0-SNAPSHOT, pyml_bindgen ships two helper scripts for dealing with this type of thing automatically: gen_multi and combine_rec_modules. Check out the Recursive Modules example on GitHub for how to use them.

Semi-manually generate bindings

The pyml_bindgen itself doesn't handle recursive modules. But it is simple enough to edit the output by hand. Let's see how.

Value specs

Since there are two classes to bind, we will make two val spec files.

foo_val_specs.txt

val make_bar : unit -> Bar.t

bar_val_specs.txt

val make_foo : unit -> Foo.t

Run pyml_bindgen

Now, run pyml_bindgen with some extra shell commands to make the output look nicer.

pyml_bindgen foo_val_specs.txt silly Foo --caml-module Foo -r no_check \
  | ocamlformat --enable --name=a.ml - > lib.ml

printf "\n" >> lib.ml

pyml_bindgen bar_val_specs.txt silly Bar --caml-module Bar -r no_check \
  | ocamlformat --enable --name=a.ml - >> lib.ml

Fix the output

If you were to try and compile that code, you'd get a lot of errors including about unknownBar module.

To fix it, change module Foo : sig to module rec Foo : sig and module Bar : sig to and Bar : sig.

Once you do that, everything will compile fine :)

Here is what the output should look like:

module rec Foo : sig

... sig ...

end = struct

... impl ...

end

and Bar : sig

... sig ...

end = struct

... impl ...

end

Using the generated modules

You can use the generated modules as you would any others.

open Lib

let () = Py.initialize ()

let (_bar : Bar.t) = Foo.make_bar ()
let (_foo : Foo.t) = Bar.make_foo ()

Wrap-up

You may come across cyclic classes when binding Python code. If you want to bind them in OCaml as it, you will need to use recursive module. This page shows you how to do it semi-manually using pyml_bindgen. If you would like a more automatic way to do this, see the Recursive Modules example on GitHub.