• 2 Posts
  • 44 Comments
Joined 1 year ago
cake
Cake day: June 15th, 2023

help-circle


  • 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
    
    *)