I’m trying to model Rust subtyping around trait objects in OCaml. I have made a universal smart pointer for ocaml-rs (repo here if someone is interested), which allows to store Rust objects or Rust trait objects within single OCaml Value, and pass it back to Rust stubs. The thing comes with dynamic type information registry which allows Rust side to coerce smart pointer to concrete type into smart pointer to trait object (only object-safe traits can be used for this). With some magic sprinkled on top, I can get a list of polymorphic variant constructors that represent which traits certain boxed object provides. What I’m trying to do is to improve developer experience and provide all methods that come from implemented traits right into final module, e.g. in example below I want to_string
method from Display
to be available directly within Error
module, as Error
trait includes Display
trait from Rust side. It’s possible to call Display.to_string
on Error.t
with explicit coercion, but that is hard to discover when glancing at methids available in Error
module in one’s IDE - one would have to go read the constructors in type definition.
module Display = struct
type nonrec t = [ `Std_fmt_display | `Core_marker_send ] Ocaml_rs_smartptr.Rusty_obj.t
external to_string : t -> string = "rstdlib_display_to_string"
end
module Error = struct
type nonrec t =
[ `Ocaml_stdlib_error_wrapper_error_wrapper
| `Core_marker_send
| `Std_error_error
| `Std_fmt_display
| `Core_fmt_debug
]
Ocaml_rs_smartptr.Rusty_obj.t
(* Attempt to include functions from Display as variant contractors are
compatible fails :(
In this `with' constraint, the new definition of t
does not match its original definition in the constrained signature:
Type declarations do not match:
type t = t
is not included in
type t =
[ `Core_marker_send | `Std_fmt_display ]
Ocaml_rs_smartptr.Rusty_obj.intf
The type
t/2 =
[ `Core_fmt_debug
| `Core_marker_send
| `Ocaml_stdlib_error_wrapper_error_wrapper
| `Std_error_error
| `Std_fmt_display ] Ocaml_rs_smartptr.Rusty_obj.intf
is not equal to the type
[ `Core_marker_send | `Std_fmt_display ] Ocaml_rs_smartptr.Rusty_obj.intf
The second variant type does not allow tag(s)
`Core_fmt_debug, `Ocaml_stdlib_error_wrapper_error_wrapper, `Std_error_errorocamllsp
*)
include (Display : module type of Display with type t := t)
external source : t -> t option = "rstdlib_error_source"
end
let () =
let error : Error.t = Obj.magic () in
(* Explicit coercion below works *)
let _ : string = Display.to_string (error :> Display.t) in
()
;;
Rusty_obj.intf
is defined as follows in Rusty_obj.mli:
type -'tags intf
type 'a t = ([> ] as 'a) intf
Probably something is wrong with that covariant/contravariant modifiers for tags? Unfortunately I can’t wrap my head around this… Is there some way to achieve inclusion of Display
module into Error
module given that constructors are compatible? I could of course write a bunch of wrapper functions by hand, but that won’t scale. Set of constructors along with external method signatures are actually generated by ocaml-gen
crate and I can’t modify it to produce something more complex from Rust side. Thus I wanted to include generated modules into my own, potentially add some convenience functions on top of generated ones, and add those include relations for convenience. Any help on this would be greatly appreciated.