Suppose I have a type consisting of multiple polymorphic variants (covariantly) such as the following:
[> `Ok of int | `Error of string]
Let's further suppose that I want to factor this definition into some kind of type constructor and a concrete type int
. My first attempt was the following:
type 'a error = [> `Ok of 'a | `Error of string]
However, using a definition like this produces a really strange type error mentioning a type variable 'b
that doesn't appear anywhere in the definition.
$ ocaml
OCaml version 4.07.0
# type 'a error = [> `Ok of 'a | `Error of string ];;
Error: A type variable is unbound in this type declaration.
In type [> `Error of string | `Ok of 'a ] as 'b the variable 'b is unbound
This 'b
is an autogenerated name, adding an explicit 'b
shifts the variable to 'c
.
$ ocaml
OCaml version 4.07.0
# type ('a, 'b) error = [> `Ok of 'a | `Error of 'b ];;
Error: A type variable is unbound in this type declaration.
In type [> `Error of 'b | `Ok of 'a ] as 'c the variable 'c is unbound
Using the invariant construction [ `Thing1 of type1 | `Thing2 of type 2 ]
appears to work fine in this context.
$ ocaml
OCaml version 4.07.0
# type 'a error = [ `Ok of 'a | `Error of string ] ;;
type 'a error = [ `Error of string | `Ok of 'a ]
#
However, explicitly marking the type parameter as covariant does not salvage the original example.
$ ocaml
OCaml version 4.07.0
# type +'a error = [> `Ok of 'a | `Error of string];;
Error: A type variable is unbound in this type declaration.
In type [> `Error of string | `Ok of 'a ] as 'b the variable 'b is unbound
And, just for good measure, adding a contravariance annotation also does not work.
$ ocaml
OCaml version 4.07.0
# type -'a error = [> `Ok of 'a | `Error of string];;
Error: A type variable is unbound in this type declaration.
In type [> `Error of string | `Ok of 'a ] as 'b the variable 'b is unbound
Attempting to guess the name that the compiler will use for the unbound type variable and adding it as a parameter on the left also does not work and produces a very bizarre error message.
$ ocaml
OCaml version 4.07.0
# type ('a, 'b) string = [> `Ok of 'a | `Error of string] ;;
Error: The type constructor string expects 2 argument(s),
but is here applied to 0 argument(s)
Is there a way of making a type constructor that can effectively "substitute different types" for int in [> `Ok of int | `Error of string]
?
Parametric polymorphismrefers to code that is written without knowledge of the actual type of the arguments; the code is parametric in the type of the parameters. Examples include polymorphic functions in ML, or generics in Java 5. We consider parametric polymorphism in more detail.
Type-checking polymorphic variants is a subtle thing, and some expressions may result in more complex type information. Here we are seeing two phenomena. First, since this matching is open (the last case catches any tag), we obtain the type [> `A | `B] rather than [< `A | `B] in a closed matching.
Ad-hoc polymorphism is a dispatch mechanism: the type of the arguments is used to determine (either at compile time or run time) which code to invoke. Parametric polymorphismrefers to code that is written without knowledge of the actual type of the arguments; the code is parametric in the type of the parameters.
A polymorphic datatype is one that can contain elements of different types. Several kinds of polymorphism are commonly used in modern languages. Subtype polymorphismgives a single term many types using the subsumption rule. For example, a function with argument˝can operate on any value with a type that is a subtype of˝.
This isn't an issue of variance, or parametric polymorphism, but of row polymorphism. When you add >
or <
it also adds an implicit type variable, the row variable, that will hold the "full" type. You can see this type variable made explicit in the error:
[> `Error of string | `Ok of 'a ] as 'b
Note the as 'b
part at the end.
In order to alias the type you have to make the type variable explicit, so you can reference it as a type parameter on the alias:
type ('a, 'r) error = [> `Ok of 'a | `Error of string ] as 'r
Note also, in case you have or when you will, run into objects, that this applies there as well. An object type with ..
has an implicit type variable that you need to make explicit in order to alias it:
type 'r obj = < foo: int; .. > as 'r
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With