How do I use a Variant with generic constructor from another module?

How do I use a Variant with generic constructor declared in another module?
Example of erroneous usage here. Is this not allowed? Or am I abusing Variants?

I’m not sure what you’re trying to do but since the variant is defined in your Colors module, you should point there when creating a variant, even if it’s of type ColorManager.

Here is your example fixed:

module Colors = {
  type t<'a> = Hex('a) | Name('a) // Variant with generic constructor
}

module ColorManager = {
  type color = Colors.t<string>

  let teal: color = Colors.Name("teal")
}

module ManageColor = {
  let teal: ColorManager.color = Colors.Name("teal")
}

Another solution is to include the Colors module:

module Colors = {
  type t<'a> = Hex('a) | Name('a) // Variant with generic constructor
}

module ColorManager = {
  include Colors
  type color = t<string>

  let teal = Name("teal")
}

module ManageColor = {
  let teal = ColorManager.Name("teal")
}

But it’s hard to know what’s best without knowing what you wan to achieve.

1 Like

To inform a decision on that, if Colors had any runtime, including it in ColorManager would result in the latter having a copy that runtime too. But if Colors only holds types, those get discarded, so it’s mostly a matter of readability.

1 Like

@tsnobip I was trying to create abstractions of similar color types - some using hex values, some using names, some using rgb values.

Anyways, my intuition was since type color = Colors.t<string> is specifying the actual Color type, I would be able to use it or refer to its module (ColorManager) to find the Variant.

Explanation by @hoichi gives a little more clarity into behind the scenes of where the types are held and when it is held.

But it sure was unintuitive(for me :sweat_smile:), since I expected the redeclaration to hold the type information, not the original source.

One more thing to note…annotating the type as tsnobip shows brings the variant into scope so that you do not need to fully qualify the variant.

playground

module Colors = {
  type t<'a> = Hex('a) | Name('a) // Variant with generic constructor
}

module ColorManager = {
  type color = Colors.t<string>
  
  // No need to qualify here.
  let teal: color = Name("teal")
  
  // Just to show you can use Colors.t directly (also without qualifying)
  let blue: Colors.t<string> = Name("blue")
}

module ManageColor = {
  // No need to qualify here.
  let teal: ColorManager.color = Name("teal")
  
  // Just to show you can use Colors.t directly (also without qualifying)
  let blue: Colors.t<string> = Name("blue")
}

By the way, avoiding qualifying constructors in this way seems to be the style suggested in the docs as well. (And for what it is worth, I’ve seen the same suggested in some OCaml style guides as well…see the last point here, if you know any OCaml.)

2 Likes