Skip Navigation

A quick guide to GADTs and why you ain't gonna need them

practicalocaml.com A quick guide to GADTs and why you ain't gonna need them

Ever wanted to use a GADT but did not know if you really needed them? You probably don't. And here's why.

4
4 comments
  • I'm no OCaml expert and mostly I'm casually browsing.

    The arguments presented read quite compelling. What are your thoughts? Does the conclusion make sense? Have you had any real world experience w/ GADTs that you could share?

    • I’ve tried something recently in a project with Dream.

      Suppose you have an url like /page/param1/param2/ : you want to be able to do three things:

      1. Build this url in the application in a safe way, which mean : param1 -> param2 -> string
      2. Create a route which handle this url pattern (the compilation should fail if you don’t handle all the arguments)
      3. Ensure the handler will have the signature param1 -> param2 -> Dream.handler

      Of course, some pages will have two arguments, some other three, and you have to find a way represent this in the type system.

      For this, I’ve used a gadt:

      
      type ('a, 'b) t =
        | Static : string * ('a, 'b) t -> (unit -> 'a, 'b) t
        | Int : string * ('a, 'b) t -> (int64 -> 'a, 'b) t
        | String : string * ('a, 'b) t -> (string -> 'a, 'b) t
        | End : ('a, 'a) t
      

      The string is the parameter name in the url (id, login, …) and the gadt make the representation for this parameter in the type system. This gives me a way to declare some urls in the application:

      val root : (unit -> 'a, 'a) t
      (** The path to root / *)
      
      val topic_url : (string -> 'a, 'a) t
      (** The path to /:topic *)
      
      val thread_url : (string -> int64 -> 'a, 'a) t
      (** The path to /:topic/:thread *)
      

      Then I have some some functions wihch handle this gadt:

      val repr : (unit -> ('a, 'b) t) -> string
      (** Represent the route associated with this path *)
      
      val unzip : (unit -> ('a, 'b) t) -> (string -> string) -> 'a -> 'b
      (** Extract the parameters from the request. 
      
          [unzip path extract f] will apply the function extract for all the
          arguments in the path, and gives them to f 
      
          The function [extract] is given in argument in order to break dependency
          circle.
      
          This should be something like: 
      
              let extract_param request name =
                Dream.param request name |> Dream.from_percent_encoded
      
      *)
      
      • Thanks for your reply. I'm still not sure if I have managed to wrap my head around this 😕 I guess I need to re-read the relevant chapter from RWO book. I'll post back here I'm finally able to understand handler in your case.