Polyglot FFI — Observations from Building FFI Bindings

I’ve been writing OCaml code that needs to interface with Python for a while, and I kept running into the same issue: every project required the same long stretch of boilerplate. ctypes declarations, C stubs with CAMLparam and CAMLreturn, Python wrappers, Dune configs. None of it was the interesting part of the work. It was just the cost of getting across the language boundary.

After repeating this enough times, I started noticing how mechanical the entire process was. You take a function signature from an .mli file and translate it into three different layers. It doesn’t really involve problem-solving. It’s just structured repetition. That was the moment I realized I should automate it.

So I built Polyglot FFI.

The goal is straightforward: write your interface once, and let the tool handle the rest.

polyglot-ffi generate crypto.mli

That one command generates the ctypes layer, the C wrappers with proper GC handling, the Python module with type hints, and the corresponding build configuration. It supports primitives, records, tuples, lists, Option, Result, and nested types.

I just released v0.5.2, which fixes some memory safety bugs I found while testing Option types. In certain cases, a NULL pointer could slip through and cause crashes. Those are the details I wanted the tool to handle, so developers don’t have to worry about them.

What I’m looking for now is feedback from the OCaml community. I’ve used this tool in my own projects, but I know there are type system edges I haven’t hit yet. I would also appreciate contributors, especially around Rust or Go support, or work on bidirectional bindings.

The project is MIT licensed.
Install with: pip install polyglot-ffi
Docs: https://polyglotffi.com
GitHub: https://github.com/chizy7/polyglot-ffi

If you’ve ever found yourself rewriting the same FFI code over and over, you might find this useful. I’d love to hear your thoughts.

4 Likes

It’s a bit unclear to me what you mean by “bidirectional bindings”. I had a cursory look and it looks that this automate exporting you OCaml code to other languages. Or did I miss something ?

1 Like

Thanks for asking. let me clarify

Right now, Polyglot FFI only supports one direction it generates everything needed so other languages (like Python) can call OCaml functions.

When I mention “bidirectional,” I’m referring to a future goal, not a current feature. The idea is to eventually support the reverse direction as well letting OCaml call into Python (or other targets), not just exporting OCaml outward.

So today the tool automates exposing OCaml code. Bidirectional would mean enabling both sides to call each other. But that part hasn’t been implemented yet, it’s just something I’d like to explore long tern

For the OCaml-Rust FFI we implement roots as (linear) values. To this purpose we developed a C library to register OCaml roots as resources (e.g. boxroot_create/boxroot_delete). Using this style, the values are passed around rooted already, and the CAML* macros are not necessary.

I am wondering whether this might be useful for handling OCaml objects in Python. The idea would be to manipulate from Python only roots obtained with boxroot_create, using the finalizer to call boxroot_delete. This could usefully be automated.

One risk is with letting OCaml manipulate Python values in the same way, since if you have an OCaml value referring to a Python value referring to an OCaml value, this makes a non-collectable cycle.

1 Like

Thanks for sharing the gitlab link and also for pointing this out. the boxroot approach is new to me but it is helpful to see how the OCaml-Rust ecosystem handles rooting safely without reliying on the CAML* macros. The model seems like it could map well onto parts of Polyglot FFI’s future plans, especially if I eventually support longer-lived OCaml values or a more interactive bridge.

I understand your warning about cycles. Right now Polyglot FFI avoids that entirely by converting values immediately at the boundary, but biderectional design would have to address exactly the issue you described. I think boxroot looks like a promising foundation, but it would definitely need careful ownership rules.

I am going to dig deeper into ocaml-boxroot and the patterns used in ocaml-rust to understand how much of that could apply here. If you have any pointers on practical pitfalls or places where the model doesn’t translate well outside of Rust, I would appreciate it.

Thanks for the insight. It definitely gives me a much better starting point for thinking about the next stage.