Source file provider_options.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
(** Type-safe heterogeneous key-value store using extensible GADTs.
Uses {!Obj.Extension_constructor.id} to identify keys at runtime and
{!Obj.magic} for type recovery. This is type-safe by construction:
two extensible variant constructors share the same [Extension_constructor.id]
if and only if they are the same constructor, which guarantees they carry
the same type parameter. This is the standard OCaml pattern for extensible
GADTs, used by {!Printexc} and other stdlib modules.
Future: OCaml 5.1+ [Type.eq] would provide a first-class type equality
witness, eliminating the need for [Obj.magic]. *)
type _ key = ..
type entry = Entry : 'a key * 'a -> entry
type t = entry list
let empty = []
let set (type a) (k : a key) (v : a) (opts : t) : t =
let kid = Obj.Extension_constructor.id (Obj.Extension_constructor.of_val k) in
let replaced = ref false in
let opts' =
List.filter_map
(fun (Entry (k', _) as e) ->
let kid' = Obj.Extension_constructor.id (Obj.Extension_constructor.of_val k') in
if Int.equal kid kid' then begin
replaced := true;
Some (Entry (k, v))
end
else Some e)
opts
in
if !replaced then opts' else Entry (k, v) :: opts
let find (type a) (k : a key) (opts : t) : a option =
let kid = Obj.Extension_constructor.id (Obj.Extension_constructor.of_val k) in
let rec go = function
| [] -> None
| Entry (k', v) :: rest ->
let kid' = Obj.Extension_constructor.id (Obj.Extension_constructor.of_val k') in
if Int.equal kid' kid then Some (Obj.magic v : a) else go rest
in
go opts
let find_exn k opts =
match find k opts with
| Some v -> v
| None -> raise Not_found