What's the OCaml way to do multiple interfaces?

At the risk of starting a flamewar, I’ll say that functors are not a great solution to emulate the dynamic dispatch in other languages.

Functors are nice for some bounded polymorphism patterns such as containers that have requirements on their type parameters (map, set, hashtable, graph, heap, etc). They’re not great when you need to express “I want a list of values that have this behavior, I don’t care what their concrete type is”.

For this, you can pick:

  • record of functions. Simple, very clear, but a bit of elbow grease in practice. That’s my go to these days for simple interfaces.
  • first class modules. A bit more annoying to consume (you have to move between the expression world and the module world, there’s always a bit of ceremony). Can be nice if you already have the features in existing modules, or if you need a lot of types in your interface.
  • objects (interface: “class type”, implementations: either “class” or direct object expressions). Can be trickier at first (and error messages can be confusing) but it’s very powerful. This is the most convenient when you need interfaces that extend other interfaces (eg a type “stream” that extends both input stream and output stream; you can cast one to the other easily and without runtime cost). Eio used to use that and an early post about it had a nice rationale about why objects were used. I think this is more controversial than the other two options but it’s surprisingly powerful if you don’t go overboard with it.
4 Likes