Make variants defined in inline functor module externally visible

Hi, I’m trying to define a functor that I can use to automatically generate helper functions for various variants. I’m having some trouble with the ergonomics and I’m not sure if what I’m attempting is possible.

Here’s a minimal reproduction of what I’m trying to achieve:

module type In = { type t }

module type Out = {
  type t
  let toIntUnsafe: t => int
  let fromIntUnsafe: int => t
}

module Make = (In: In): (Out with type t = In.t) => {
  type t = In.t

  external toIntUnsafe: t => int = "%identity"
  external fromIntUnsafe: int => t = "%identity"
}

module Example = {
  include Make({
    type t = Foo | Bar | Baz
  })
}

let usage1: Example.t = %raw(`1`)
let usage2: Example.t = Example.Foo

But when running this I get the error The variant constructor Example.Foo can't be found.

I’ve tried changing the usage to:

module Example = {
  module Def = {
    type t = Foo | Bar | Baz
  }
  include Def
  include Make(Def)
}

But then I get the error: Multiple definition of the type name t. I want to avoid using type names other than t, as I’m also using spice to auto-generate (de)serializers for these types, and the functions it generates are named after the type: (@spice type t generates t_encode, t_decode).

I’d much prefer to use the first syntax, as I think it’s much clearer and cleaner. Is there a way to access variants defined in a module that’s immediatly passed into a functor? Any help much apreciated

1 Like

It’s a bit ugly, but you can do something like this

module Example = {
  type _t = Foo | Bar | Baz
  include Make({
    type t = _t
  })
}

Duplicated type names are not allowed within the module, so make typename more specific or do not expose from the Make

module type Out = {
  type t
  let toIntUnsafe: t => int
  let fromIntUnsafe: int => t
}

module Make = (In: In) => {
  external toIntUnsafe: In.t => int = "%identity"
  external fromIntUnsafe: int => In.t = "%identity"
}
1 Like