Search code examples
pattern-matchingocamlencapsulationpolymorphic-variants

How to match private polymorphic variant type outside of containing module?


I can close the ability to create an instance of variant type outside the containing module with a private keyword.

module Ordinary : sig
  type t = private X | Y
  val x : t
end = struct 
  type t = X | Y 
  let x = X
end

I can't create instances of Ordinary.t and the following example is uncompilable:

let f x = if x = Ordinary.X then 1 else 2

Error: Cannot create values of the private type Ordinary.t

But I can match Ordinary.t and the following function works fine:

let f a = function
  | Ordinary.X -> 1
  | Ordinary.Y -> 2

For me, it is logically correct and I expect the same behavior from polymorphic variants. I created the analogical module for this case too.

module Polymorphic : sig
  type t = private [`X | `Y]
  val x : t
end = struct 
  type t = [`X | `Y]
  let x = `X
end

But I can't match Polymorphic.t. All my tries with error messages are shown below:

let g a = 
  match a with
  | `X -> 1 
  | `Y -> 2
    
let x = g Polymorphic.x
let x = g Polymorphic.x
          ^^^^^^^^^^^^^

Error: This expression has type Polymorphic.t but an expression was expected of type [< `X | `Y ]

let x = match Polymorphic.x with
  | `X -> 1
  | `Y -> 2 
| `X -> 1
  ^^

Error: This pattern matches values of type [? `X ] but a pattern was expected which matches values of type Polymorphic.t

let x = match Polymorphic.x with
  | Polymorphic.`X -> 1
  | Polymorphic.`Y -> 2 
| Polymorphic.`X
              ^

Error: Syntax error

let x = 
  let open Polymorphic in
  match x with
  | `X -> 1
  | `Y -> 2
| `X -> 1
  ^^

Error: This pattern matches values of type [? `X ] but a pattern was expected which matches values of type Polymorphic.t

Is it possible to match the private polymorphic variant type outside of the declaration container?
If it is - How? If it is not - Why?


Solution

  • The essence of private types is that they are private subtypes of a visible type. In particular values of the private type can be coerced with :> to the visible type.

    So, this works for me:

    # match (Polymorphic.x :> [`X|`Y]) with
      | `X -> 1
      | `Y -> 2
      ;;
    - : int = 1
    

    For what it's worth, I think of a polymorphic variant value like `X to be pervasive, like the number 1234. So `X and `Y aren't so much defined in Polymorphic as referenced there. I.e., there is no special Polymorphic.(`X) value that's different from the unqualified value `X.

    But there is a type Polymorphic.t that's particular to the module. Values of this type can be coerced to [ `X | `Y ] but not the other way around.

    I hope my way of looking at this isn't too wrong :-) And I hope this is helpful.