Is there someplace a bestiary of GADT examples ? I’d like to find such a thing in order to more-fully understand the universe of possibilities for using GADTs in programming. I’ve never used 'em, and while, sure, reading about the theory is great, and reading a few example is cool, it would be complementary to have a bottom-up understanding (lots of examples) as well as a top-down one.

4 Likes

We do seem to have a home-grown ‘GADT’-iary: Open source projects using GADTs

A couple of new and interesting ones since that thread:

8 Likes

Not exactly examples of GADTs “in the wild”, but I’ve found @yallop 's slides (from the Advanced Functional Programming course at Cambridge) to be interesting for seeing “GADT design patterns”: https://www.cl.cam.ac.uk/teaching/1617/L28/lecture-8-slides.pdf , https://www.cl.cam.ac.uk/teaching/1617/L28/lecture-9a-slides.pdf. (I hope it is OK to link those here!)

3 Likes

I haven’t got round to properly publicising it, but I actually wrote blog post about the internal development process of Petrol, and how I gradually moved from Caqti, to macros, to GADTs which may be useful:

https://gopiandcode.uk/logs/log-ways-of-sql-in-ocaml.html

4 Likes

We use a lot of GADTs in Coq
For instance coq/tac2ffi.ml at 14946eb0cbce09c1a63d36aac21ccb1161fbd869 · coq/coq · GitHub used to have arbitrary-arity functions
Slightly simplified:

``````type ('arg,'result,'f) arity =
| One : ('arg, 'result, 'arg -> 'result) arity
| More : ('arg, 'result, 'f) arity -> ('arg, 'result, 'arg -> 'f) arity

type ('arg, 'result) nary = Nary : ('arg, 'result, 'f) arity * 'f -> ('arg, 'result) nary

type value =
| Closure of (value, value) nary
| SomeInt of int

let to_nary = function
| Closure f -> f
| SomeInt _ -> failwith "can't apply someint"

let rec apply : type f. (value,value,f) arity -> f -> value list -> value =
fun arity f args -> match args, arity with
| [], _ -> Closure (Nary (arity, f))
| [arg], One -> f arg
| arg :: args, More arity -> apply arity (f arg) args
| arg :: args, One ->
let f = f arg in
let Nary (arity, f) = to_nary f in
apply arity f args

let apply_val f args =
let Nary (arity, f) = to_nary f in
apply arity f args

let addf x y = match x, y with
| SomeInt x, SomeInt y -> SomeInt (x + y)
| _ -> failwith "addf got non-ints"

Closure (Nary (One, fun x -> Closure (Nary (One, fun y -> addf x y))))

let () = assert
(apply_val addval [SomeInt 1; SomeInt 2] =
apply_val addval' [SomeInt 1; SomeInt 2])

let () = assert
(apply_val addval [SomeInt 1; SomeInt 2] =
SomeInt 3)
``````

Or coq/evd.ml at 14946eb0cbce09c1a63d36aac21ccb1161fbd869 · coq/coq · GitHub
a record where some fields are nontrivial if and only if some other field is nontrivial

or around coq/grammar.ml at 14946eb0cbce09c1a63d36aac21ccb1161fbd869 · coq/coq · GitHub which replaced some Obj.magic in the camlp5 engine this file is derived from.

I guess you can find more by looking at https://github.com/search?q=repo%3Acoq%2Fcoq+GADT&type=commits

`'a Lwt.t` promise states and several other types in Lwt are internally GADTs. See lwt/lwt.ml at cc05e2bda6c34126a3fd8d150ee7cddb3b8a440b · ocsigen/lwt · GitHub. This is mainly to use the existential types capability of GADTs.

2 Likes

You can also check a protocol implementation with GADT here (which proves that a client should never send something to another client): bob/state.ml at main · dinosaure/bob · GitHub. A detailled article is available here: Dinosaure's website - GADTs and state machine

Also, I re-implemented a `printf` function with a promotion mechanism of certains values (à la C) here: conan/fmt.ml at main · mirage/conan · GitHub. You can also check the implementation of the decision tree which helps us to regognize MIME type, it’s a GADT too: conan/tree.ml at main · mirage/conan · GitHub.

Finally, you can check this typed lambda calculus: mirage-lambda/typedtree.ml at a89b265b552f8b63ff725fc942f41a276fabb4f5 · mirage/mirage-lambda · GitHub and the transformation from a simple lambda-calculus with a typed one (and where we prove that variables are bounds via the De-Bruijn indice).

EDIT: Ah and probably the most complicated GADT I ever see which has a real application, a zipper on an AST where the path is a GADT.

2 Likes

Menhir generates code with gadts using the method described in this paper

In that case, the specific technique used is ADTs without allocation, of which a more simple example is the following :

``````type 'a number =
| Float : float number
| Int : int number

let show_number : type n. n number -> n -> string =
fun witness n ->
match witness with
| Float -> string_of_float n
| Int -> string_of_int n
``````
2 Likes

In owi, to allow the user to define host functions usable from Wasm, we also use GADTs.

1 Like

Hi @Chet_Murthy !
Arthur Wendling (Tarides) has an implementation of Kaplan/Tarjan 99 (Purely Functional, Real-Time Deques with Catenation), which makes a non-trivial use of OCaml’s GADT. See https://github.com/art-w/deque

4 Likes

I have used GADTs in rotor to implement a rich identifier type for different syntactic elements of the OCaml language, as well as some custom zipper types over the OCaml compiler typed AST.

1 Like

I’ve revisited Typing Tricks: Diff lists various times for implementing heterogeneous lists and maps.

edit: found some related libs in this comment.