Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining parametric polymorphism and polymorphic variants (backtick types)

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]?

like image 716
Gregory Nisbet Avatar asked Jan 20 '20 23:01

Gregory Nisbet


People also ask

What is an example of parametric polymorphism?

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.

What is type-checking polymorphic variants?

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.

What is the difference between ad-hoc polymorphism and parametric polymorphism?

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.

What is a polymorphic datatype?

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˝.


1 Answers

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
like image 120
glennsl Avatar answered Oct 23 '22 14:10

glennsl