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

(** {1:tuning Tuning} *)

(**/**)

(** Default for the initial size when creating a cache. *)
let default_cache_size = 512

(** Current value for the initial cache size. *)
let initial_cache_size = ref default_cache_size

(**/**)

(** Get the value used as an initial size when creating a cache. *)
let get_initial_cache_size () = !initial_cache_size

(** Set the value used as an initial size when creating a cache. *)
let set_initial_cache_size size = initial_cache_size := size

(** Reset to default the value used as an initial size when creating a cache. *)
let reset_initial_cache_size () = initial_cache_size := default_cache_size

(**/**)

(** [mk_memo create find add ff] gives a memoïzed version of the functional [ff]
    using the functions [create], [find] and [add] for the cache. It's used
    internally and you shouldn't have to use it. *)
let mk_memo create find add ff =
  let cache = create (get_initial_cache_size ()) in
  let rec f k =
    try find cache k
    with Not_found ->
      let v = ff f k in
      add cache k v ; v
  in
  f

(**/**)

(** {1:generic Generic interface} *)

(** [memo ff] gives you a memoïzed version of the [ff] functional. *)
let memo ff =
  let open Hashtbl in
  mk_memo create find add ff

(** {1:functors Functorial interface } *)

(** The output signature of the functors {!module:Mk}, {!module:Make}, {!module:MakeWeak} and {!module:Fake}.*)
module type S = sig
  type t

  val memo : ((t -> 'a) -> t -> 'a) -> t -> 'a
end

(** With the {!module:Mk} functor, you can also directly provide a [Cache] module, which
    should have the signature [Hashtbl.S]. We will include your cache module and
    use it to define a [memo] function. It should be useful only if you want to
    use another [Hashtbl] implementation or things like this. *)
module Mk (Cache : Hashtbl.S) = struct
  include Cache

  let memo ff = mk_memo Cache.create Cache.find Cache.add ff
end

(** Functor that can be useful in case you don't want to use polymorphic
    equality or you are doing things like hashconsing and you know how to compare
    or hash your type more efficiently. *)
module Make (H : Hashtbl.HashedType) = Mk (Hashtbl.Make (H))

(** Functor that works like the [Make] one, but the bindings in the memoïzation
    cache will be weak, allowing the garbage collector to remove them if they are
    not used somewhere else. *)
module MakeWeak (H : Hashtbl.HashedType) = Mk (Ephemeron.K1.Make (H))

(** Functor that is useful if you want to quickly test a function you memoïzed
    with our {!module:Make} or {!module:MakeWeak} functor, but without memoïzing it. It'll
    basically do nothing and should be equivalent to your initial non-memoïzed
    function. *)
module Fake (H : Hashtbl.HashedType) = Mk (struct
  include Hashtbl.Make (H)

  let find _ _ = raise_notrace Not_found

  let add _ _ _ = ()
end)