Hi everyone, Section 7.6 of the manual gives a syntax to explicitly name existential types in GADTs, but it doesn’t say what to do when the type is not an existential type. Here’s a snippet that I would want to work. How could I write it differently in order for it to work?

type _ t =
| A: 'a -> ('a * 'a) t
let f: type a. a t -> a =
fun x ->
begin match x with
| A (type u) (a:u) -> (a, a)
end

Here the code is correct except for the typing annotation, and it works when you remove it, but this is a MWE, and in my actual case (something involving let module), I absolutely need the typing annotation.

You should extend your example to cover your use case.

Without more information, it sounds likely that something similar to:

let a_branch (type a) (x:a) =
let module M = Set.Make(struct type t = a * a let compare = Stdlib.compare end) in
x, x
let f : type a. a t -> a =fun (A x) -> a_branch x

It doesn’t explain the really cryptic error message:

let f : type a. a t -> a = function
| A (type u) (x : u) -> (x, x)
;;
Error: This pattern matches values of type u
but a pattern was expected which matches values of type $0

I believe the purpose of the introduction of this syntax was to avoid the type $0 in error messages, and why u and $0 don’t unify?

@gasche : if I remember correctly you introduce this new syntax, but I don’t understand why it doesn’t work in this case. Why the type variables u and $0 don’t unify? It puzzles me…
To be clear, what I was expecting is that u and $0 unify and then that a unifies with u * u.

I agree that the error message is bad, and that one could expect this example to work.

I would also expect the following to work:

type _ th =
| Thunk : 'a * ('a -> 'b) -> 'b th
let f (type t) : t th -> t = function
| Thunk (type a b) (x, f : a * (a -> b)) -> f x

and it currently does not. So maybe the implementation needs refining.

Currently it looks like only “pure existentials” can be named this way, not existentials that are equated with a part of the rigid type variable being refined with an equality. But I don’t think that we should expect users to make this distinction, they probably want to name all the variables local to the GADT constructor in the type declaration.

I think we should open an upstream issue to track this limitation of the current implementation.

(This is probably a sign that not too many people have tried to use it yet, otherwise I think they would have reported similar confusions?)

Note: I did not introduce the new syntax, this is work from @garrigue.

Indeed, that’s what I realized after more reflection. I guess the type checker first type the anonymous lambda with ('a -> 'b) -> 'a -> 'b and then apply this scheme with the local type of the GADT.

By the way, there may be some problem also with local abstract type unrelated to GADT. This example, close to yours, give me a weird error message:

fun (type a b) (x,f : a * a -> b) -> f x;;
Error: This pattern matches values of type 'a * 'b
but a pattern was expected which matches values of type a * a -> b

(We could possibly stop assuming that people are familiar with the respective precedence of * and -> in the type printer, or think of way of mentioning the head type constructors that clash in addition to showing the full types.)

Maybe. To me -> is an exponential and it should have precedence over product. I wrote 5 * 3 ^ 2 to mean 5 * 9 and not 15 ^ 2.

Edit: in reality, it’s even worse. When you see -> as an exponential, 3 ^ 2 should be written 2 -> 3. And so, with the precedence rules of Ocaml, instead of 5 * 9, I get: