Source file type.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
type (_,_) eq = Refl: ('a,'a) eq

type 'a witness = ..
type 'a eq_witness = {eq: 'b . 'b witness -> ('a, 'b) eq option }
type 'a t = 
  | NULLABLE: 'a t -> 'a option t
  | INTEGER : int t
  | REAL: float t
  | TEXT : string t
  | BOOLEAN: bool t
  | CUSTOM : {
      ty: 'a Caqti_type.t;
      witness: 'a witness;
      eq_witness: 'a eq_witness;
      repr: string;
    } -> 'a t

let custom : 'a . ty:'a Caqti_type.t -> repr:string -> 'a t =
  fun (type a) ~(ty:a Caqti_type.t) ~repr : a t ->
  let module M = struct
    type _ witness += CUSTOM : a witness
    end in
  let eq_witness : 'b . 'b witness -> (a, 'b) eq option =
    fun (type b) (witness: b witness) : (a, b) eq option ->
      match witness with
      | M.CUSTOM -> Some Refl
      | _ -> None in
  CUSTOM {
    ty;
    witness=M.CUSTOM;
    eq_witness={eq=eq_witness};
    repr;
  }

type _ ty_list =
  | Nil : unit ty_list
  | Cons : 'a t * 'b ty_list -> ('a * 'b) ty_list

module Numeric = struct

  type 'a integral = Int : int integral | Int32 : int32 integral | Int64 : int64 integral

  type 'a t = Int: int t | Int32 : int32 t | Int64 : int64 t | Real: float t

end

module Postgres = struct

  type 'a witness += BIGINT : int64 witness
  let big_int = CUSTOM { ty = Caqti_type.int64; repr = "BIGINT"; witness=BIGINT;
                         eq_witness={eq=
                                       fun (type b) (witness: b witness) : (int64, b) eq option ->
                                         match witness with
                                         | BIGINT -> Some Refl
                                         | _ -> None
                                    }}

  type 'a witness += BIGSERIAL : int64 witness
  let big_serial = CUSTOM { ty = Caqti_type.int64; repr = "BIGSERIAL"; witness=BIGSERIAL;
                            eq_witness={eq=fun (type b) (witness: b witness) : (int64, b) eq option ->
                              match witness with
                              | BIGSERIAL -> Some Refl
                              | _ -> None}}
  type 'a witness += BYTEA : string witness
  let bytea  = CUSTOM { ty = Caqti_type.string; repr = "BYTEA"; witness=BYTEA;
                        eq_witness={eq=fun (type b) (witness: b witness) : (string, b) eq option ->
                          match witness with
                          | BYTEA -> Some Refl
                          | _ -> None}}

  type 'a witness += CHARACTER : int -> string witness
  let character (n: int) = CUSTOM { ty = Caqti_type.string; repr = Format.sprintf "CHARACTER(%d)" n; witness=CHARACTER n;
                                    eq_witness={eq=fun (type b) (witness: b witness) : (string, b) eq option ->
                                      match witness with
                                      | CHARACTER n' when n = n' -> Some Refl
                                      | _ -> None}}
  type 'a witness += CHARACTER_VARYING : int -> string witness
  let character_varying n = CUSTOM { ty = Caqti_type.string; repr = Format.sprintf "CHARACTER VARYING(%d)" n; witness=CHARACTER_VARYING n;
                                     eq_witness={eq=fun (type b) (witness: b witness) : (string, b) eq option ->
                                       match witness with
                                       | CHARACTER_VARYING n' when n = n' -> Some Refl
                                       | _ -> None}}

  type 'a witness += DATE : Ptime.t witness
  let date = CUSTOM { ty = Caqti_type.Std.pdate; repr = "DATE"; witness=DATE;
                      eq_witness={eq=fun (type b) (witness: b witness) : (Ptime.t, b) eq option ->
                        match witness with
                        | DATE -> Some Refl
                        | _ -> None}}

  type 'a witness += DOUBLE_PRECISION : float witness
  let double_precision = CUSTOM { ty = Caqti_type.float; repr = "DOUBLE PRECISION"; witness=DOUBLE_PRECISION;
                                  eq_witness={eq=fun (type b) (witness: b witness) : (float, b) eq option ->
                                    match witness with
                                    | DOUBLE_PRECISION -> Some Refl
                                    | _ -> None}}

  type 'a witness += INT4 : int32 witness
  let int4 = CUSTOM { ty = Caqti_type.int32; repr = "INT4"; witness=INT4;
                      eq_witness={eq=fun (type b) (witness: b witness) : (int32, b) eq option ->
                        match witness with
                        | INT4 -> Some Refl
                        | _ -> None}}

  type 'a witness += SMALLINT : int witness
  let smallint = CUSTOM { ty = Caqti_type.int16; repr = "SMALLINT" ; witness=SMALLINT;
                          eq_witness={eq=fun (type b) (witness: b witness) : (int, b) eq option ->
                            match witness with
                            | SMALLINT -> Some Refl
                            | _ -> None}}

  type 'a witness += SMALLSERIAL : int witness
  let smallserial = CUSTOM { ty = Caqti_type.int16; repr = "SMALLSERIAL";witness=SMALLSERIAL;
                             eq_witness={eq=fun (type b) (witness: b witness) : (int, b) eq option ->
                               match witness with
                               | SMALLSERIAL -> Some Refl
                               | _ -> None}}

  type 'a witness += TIME : Ptime.t witness
  let time = CUSTOM { ty = Caqti_type.Std.ptime; repr = "TIME"; witness=TIME;
                      eq_witness={eq=fun (type b) (witness: b witness) : (Ptime.t, b) eq option ->
                        match witness with
                        | TIME -> Some Refl
                        | _ -> None}}

end

module Sqlite3 = struct

  type 'a witness += BLOB: string witness
  let blob = CUSTOM { ty = Caqti_type.string; repr = "BLOB"; witness=BLOB;
                      eq_witness={eq=fun (type b) (witness: b witness) : (string, b) eq option ->
                        match witness with
                        | BLOB -> Some Refl
                        | _ -> None}}

end

let int = INTEGER
let real = REAL
let text = TEXT
let bool = BOOLEAN

let null_ty : 'a . 'a t -> 'a option t =
  fun (type a) (ty: a t) : a option t ->
  match ty with
  | INTEGER -> NULLABLE INTEGER
  | REAL -> NULLABLE REAL
  | TEXT -> NULLABLE TEXT
  | BOOLEAN -> NULLABLE BOOLEAN
  | CUSTOM def -> NULLABLE (CUSTOM def)
  | NULLABLE _ -> invalid_arg "already a nullable type"

let rec ty_to_caqti_ty: 'a . 'a t -> 'a Caqti_type.t =
  fun (type a) (ty: a t) : a Caqti_type.t ->
  match ty with
  | INTEGER -> Caqti_type.int
  | BOOLEAN -> Caqti_type.bool
  | REAL -> Caqti_type.float
  | TEXT -> Caqti_type.string
  | CUSTOM {ty;_} -> ty
  | NULLABLE ty -> Caqti_type.option (ty_to_caqti_ty ty)

let rec ty_list_to_caqti_ty: 'a . 'a ty_list -> 'a Caqti_type.t =
  fun (type a) (ls: a ty_list) : a Caqti_type.t ->
  match ls with
  | Nil -> Caqti_type.unit
  | Cons (h, t) ->
    Caqti_type.tup2 (ty_to_caqti_ty h) (ty_list_to_caqti_ty t)

let rec eq_ty: 'a 'b . 'a t * 'b t -> ('a,'b) eq option =
  fun (type a b) ((e1,e2): a t * b t) : (a,b) eq option ->
  match e1,e2 with
  | (INTEGER, INTEGER) -> Some Refl
  | (REAL, REAL) -> Some Refl
  | (TEXT, TEXT) -> Some Refl
  | (CUSTOM {ty=_; repr=_;eq_witness;_}, CUSTOM {ty=_; repr=_; witness;_}) ->
    eq_witness.eq witness
  | (NULLABLE l, NULLABLE r) -> begin match eq_ty (l,r) with
    | Some Refl -> Some Refl
    | None -> None
  end
  | _ -> None

let rec eq_ty_list : 'a 'b . 'a ty_list * 'b ty_list -> ('a,'b) eq option =
  fun (type a b) ((l1,l2): (a ty_list * b ty_list)) : (a,b) eq option ->
  match l1,l2 with
  | Nil, Nil -> Some Refl
  | Cons (h1,t1), Cons (h2, t2) -> begin
      match eq_ty (h1,h2) with
      | Some Refl -> begin match eq_ty_list (t1, t2) with
        | Some Refl -> Some Refl
        | None -> None
      end
      | None -> None
    end
  | _ -> None

let rec show : 'a . 'a t -> string =  fun (type a) (ty: a t) ->
  match ty with
  | INTEGER -> "INTEGER"
  | REAL -> "REAL"
  | BOOLEAN -> "BOOLEAN"
  | TEXT -> "TEXT"
  | CUSTOM {repr;_} -> repr
  | NULLABLE ty -> show ty

let rec pp_value : 'a . 'a t -> Format.formatter -> 'a -> unit =  fun (type a) (ty: a t) fmt (vl: a) ->
  match ty with
  | INTEGER -> Format.fprintf fmt "%d" vl
  | REAL -> Format.fprintf fmt "%f" vl
  | BOOLEAN -> Format.fprintf fmt "%b" vl
  | TEXT -> Format.fprintf fmt "%S" vl
  | CUSTOM {ty;_} -> Caqti_type.pp_value fmt (ty,vl)
  | NULLABLE ty ->
    match vl with
    | None -> Format.fprintf fmt "NULL"
    | Some vl -> pp_value ty fmt vl

module Map (S: sig type ('key,'vl) t end) : sig
  type !'a t
  val empty: 'a t

  val lookup_opt : 'opt t -> key:'k ty_list -> ('k, 'opt) S.t option

  val insert : 'a t -> key:'k ty_list -> data:('k, 'a) S.t -> 'a t
end = struct

  type !'a t =
    | [] : 'a t
    | (::) : ('k ty_list * ('k, 'a) S.t) * 'a t -> 'a t

  let empty : 'a t = []

  let rec lookup_opt: 'a t -> key:'k ty_list -> ('k,'a) S.t option =
    let handle: 'a 'b . 'b ty_list -> 'a ty_list * ('a, 'opt) S.t -> ('b, 'opt) S.t option =
      fun (type a b) (l1: b ty_list) ((l2,data): (a ty_list * (a, 'opt) S.t)) : (b, 'opt) S.t option ->
        match eq_ty_list (l1, l2) with
        | Some Refl -> Some data
        | _ -> None in
    fun ls ~key ->
      match ls with
      | [] -> None
      | data :: t ->
        match handle key data with
        | Some data -> Some data
        | None -> lookup_opt t ~key

  let insert : 'a t -> key:'k ty_list -> data:('k,'a) S.t -> 'a t =
    fun ls ~key ~data -> (key,data) :: ls
end