MLton 20210117

# EqualityTypeVariable

An equality type variable is a type variable that starts with two or more primes, as in ''a or ''b. The canonical use of equality type variables is in specifying the type of the PolymorphicEquality function, which is ''a * ''a -> bool. Equality type variables ensure that polymorphic equality is only used on equality types, by requiring that at every use of a polymorphic value, equality type variables are instantiated by equality types.

For example, the following program is type correct because polymorphic equality is applied to variables of type ''a.

    fun f (x: ''a, y: ''a): bool = x = y

On the other hand, the following program is not type correct, because polymorphic equality is applied to variables of type 'a, which is not an equality type.

    fun f (x: 'a, y: 'a): bool = x = y

MLton reports the following error, indicating that polymorphic equality expects equality types, but didn’t get them.

    Error: z.sml 1.30-1.34.
      Function applied to incorrect argument.
        expects: [<equality>] * [<equality>]
        but got: ['a] * ['a]
        in: = (x, y)

As an example of using such a function that requires equality types, suppose that f has polymorphic type ''a -> unit. Then, f 13 is type correct because int is an equality type. On the other hand, f 13.0 and f (fn x => x) are not type correct, because real and arrow types are not equality types. We can test these facts with the following short programs. First, we verify that such an f can be applied to integers.

    functor Ok (val f: ''a -> unit): sig end =
       struct
          val () = f 13
          val () = f 14
       end

We can do better, and verify that such an f can be applied to any integer.

    functor Ok (val f: ''a -> unit): sig end =
       struct
          fun g (x: int) = f x
       end

Even better, we don’t need to introduce a dummy function name; we can use a type constraint.

    functor Ok (val f: ''a -> unit): sig end =
       struct
          val _ = f: int -> unit
       end

Even better, we can use a signature constraint.

    functor Ok (S: sig val f: ''a -> unit end):
       sig val f: int -> unit end = S

This functor concisely verifies that a function of polymorphic type ''a -> unit can be safely used as a function of type int -> unit.

As above, we can verify that such an f can not be used at non-equality types.

    functor Bad (S: sig val f: ''a -> unit end):
       sig val f: real -> unit end = S

    functor Bad (S: sig val f: ''a -> unit end):
       sig val f: ('a -> 'a) -> unit end = S

MLton reports the following errors.

    Error: z.sml 2.4-2.30.
      Variable in structure disagrees with signature (type): f.
        structure: val f: [<equality>] -> _
        defn at: z.sml 1.25-1.25
        signature: val f: [real] -> _
        spec at: z.sml 2.12-2.12
    Error: z.sml 5.4-5.36.
      Variable in structure disagrees with signature (type): f.
        structure: val f: [<equality>] -> _
        defn at: z.sml 4.25-4.25
        signature: val f: [_ -> _] -> _
        spec at: z.sml 5.12-5.12

## Equality type variables in type and datatype declarations

Equality type variables can be used in type and datatype declarations; however they play no special role. For example,

    type 'a t = 'a * int

is completely identical to

    type ''a t = ''a * int

In particular, such a definition does not require that t only be applied to equality types.

Similarly,

    datatype 'a t = A | B of 'a

is completely identical to

    datatype ''a t = A | B of ''a