1

I've stumbled onto 'GADT's in Ocaml. They look interesting and powerful, but they are a bit of a head scratcher.

As I'm reading through this 'tutorial' here... it reads mostly like giberish to me. But let's try and focus a question out of that to make some headway.

It gives an example of a simple expression language.

type _ term =
  | Int : int -> int term
  | Add : (int -> int -> int) term
  | App : ('b -> 'a) term * 'b term -> 'a term

The motivation for this (similar if not identical example) and why GADTs are good/useful is actually better explained in the Real World Ocaml book. It sort of makes sense.

But neither of these sources really explains the meaning of some 'special' type declaration syntaxes for recursive functions on GADTs that they are using, and that seem crucial into having things type-check and compile.

From ocaml manual/tutorial:

let rec eval : type a. a term -> a = function
  | Int n    -> n                 (* a = int *)
  | Add      -> (fun x y -> x+y)  (* a = int -> int -> int *)
  | App(f,x) -> (eval f) (eval x)
          (* eval called at types (b->a) and b for fresh b *)

And it then says:

It is important to remark that the function eval is using the polymorphic syntax for locally abstract types.

What on earth is a 'polymorphic syntax for locally abstract types' I guess they are talking about the strange type a. notation, but that still doesn't tell me what that notation actually means.

And they compare/contrast this with

let rec eval (type a) : a term -> a = function ...

This looks almost the same to me. But I suppose it means something subtly different.

For good measure, I can add one more 'similar' but more familiar looking thing that probably still means something different still:

let rec eval : 'a term -> 'a

Can someone explain what exactly each of these three notations means and how they are saying something (subtly) different from each other (naively... to me they all look similarly 'polymorphic' as they are seemingly saying the same thing (translated into informal English):

eval takes a term of type a and returns a value of type a

Kris
  • 3,898
  • 1
  • 23
  • 32
  • 1
    can you read through [this](https://stackoverflow.com/questions/69144536/in-ocaml-what-is-the-difference-between-a-and-type-a-and-when-to-use-eac) and see if you can get around something ? – Nalin Ranjan Apr 30 '22 at 08:28
  • 1
    @glennsl suspect that it does (answer the question), but it is also very hard to digest. Maybe not quite as hard as the 'tutorial' from the manual. The problem I have is that nobody really comes out and says what the 'meaning' of these type declarations really is. It just says things like "this allows you to do". While that is helpful, I feel a bit lost still describing to myself what these phrases 'mean'. I.e. I wish for some 'simple' sentences like the one I have at the end of my question, but made 'more precise'. Perhaps the concepts are just too complex to describe that way. – Kris Apr 30 '22 at 14:43
  • I think the problem is that locally abstract types are more of a historical accident than a neat and well-defined feature. So to really understand why ti exists you'd have to delve into compiler internals and the history of the compiler. From what I understand it's both possible and desired to unify these concepts, but there's not enough time and it's not very high on the list of priorities, since it works, albeit a bit awkwardly, and since it's not exactly beginner-level a bit of awkwardness is seen as tolerable. – glennsl Apr 30 '22 at 16:28
  • 1
    No locally abstract types are not an accident. They are perfectly well defined: their semantic is stated in their name. (There is really no need to delve in the internal of the compiler to understand them). The issue is more on the side of unification type variable whose semantics often trips people. – octachron Apr 30 '22 at 17:16
  • 1
    Just to clarify my confusion @octachron "their semantic is stated in their name". Okay... so how are they 'abstract' ? I mean how can something be more abstract than a type variable `'a` (it literally means 'a type you know nothing about' already). Also 'locally' means 'in some context', so the question is 'local to what' exactly. So both 'local' and 'abstract' don't define any meaning to me as it is, without further explanation. – Kris Apr 30 '22 at 20:54
  • No an unification type variable `'a` only means "let call this type `'a`" nothing more. In particular, `let f (([] | _ :: _) :'a) (y:'a) = () ` is valid. In other words, a type variable "`'a` is neither a type nor abstract. The type is local in the sense that it lives in the scope of the binder: `let u = ref None let f (type a) (x:a) = r := Some x` is ill-typed because it would make the local type `a` escapes its scope. The name "locally abstract type" really reflect what is going on, even if it need to be better explained for beginners. – octachron Apr 30 '22 at 21:24
  • But yes, I absolutely agree that it is knowledge scaffolding issue, the name feels obvious once one has the right mental model for `abstract type` and `locally` but that doesn't help people build a good mental model. – octachron Apr 30 '22 at 21:28
  • @octachron It is quite a bit more than just a locally scoped abstract type though, because it interacts with its surroundings in ways that are not very obvious. There's implicit type equalities, there's type narrowing, there's a conceptual overlap with unification type variables (which might very well be more of a an issue with the design of unification type variables, but they came first), and I'm sure there's other aspects to it that I don't yet understand as well. If this had all been designed up-front, I do not think this would have been the end result, hence the "historical accident". – glennsl May 01 '22 at 20:52
  • Implicit type equalities are an interaction of abstract types and GADTs. There is nothing special with locally abstract types here. I could give you an example in a separate question? Type narrowing only happen on the right hand side of `as` pattern in OCaml. Anyway if you find a behavior that cannot be explained by the conjunction of local and abstract type, it would be an interesting question (and there is far too much comments on this question already). – octachron May 01 '22 at 21:38

0 Answers0