You do the same thing as in Rust, Scala or Haskell and derive the printer [1]. Then at the callsite, if you know the type then you do `T.show` to print it or `T.eq`. If you don't know the type, then you pass it in at the top level as a module and then do `T.show` or `T.eq`.
> Or to convert one type into another type?
If you want to convert a type, then you have a type that you want to convert from such as foo and bar, then you do `Foo.to_bar value`.
We can keep going, but you can get the point.
You _can't_ judge a language by doing what you want to do with one language in another. If I judge Rust by writing recursive data structures and complaining about performance and verbosity that's not particularly fair correct? I can't say that Dart is terrible for desktop because I can't use chrome developer tools on its canvas output and ignore it's hot-reloading server. I can't say Common Lisp code is unreadable because I don't have type annotations and ignore the REPL for introspection.
It indicates that you haven't written one? That's a rather strange attitude, for sure it doesn't have a wrapper for any rest api, what it has is all needed tools for write it, that's what really matters. The tool in repo above does not even use a library, only generated bindings. It's much easier to write a client to a rest api in OCaml than in Go due to derivers [1], atdgen [2] and the power of the language. Take a look at graphql bindings as an example [3] [4].
[1] https://github.com/ocaml-ppx/ppx_deriving
[2] https://github.com/mjambon/atd