Maintaining global list across modules


#1

Hello,

I have two modules a.ml and b.ml

in a.ml I have a global ref list -

let l = ref []

(*** List.length ***)

let lengthRes = fun ?(pind=(-1)) () ->
let name = “length” in

let my_features = [] in
resolveAndPacLearnSpec my_features l

(*** List.hd ***)
let hdRes = fun ?(pind=(-1)) () ->
let name = “hd” in

let my_features = [] in
resolveAndPacLearnSpec my_features l
;;

let () =
let run = (fun ((s, f) : (string * (?pind:int -> unit -> 'a))) ->
output_string stderr ("\n\n=== (" ^ (string_of_int FUNCTION_INDEX) ^ “) " ^ s ^ " ===\n”) ;
print_specs stderr (f ~pind:POST_INDEX ())) in
run (List.nth [ (“List.length(l)”, lengthRes) ;
(“List.hd(l)”, hdRes) ;] FUNCTION_INDEX)

let () = List.iter (fun s -> printf “\nl:%d\n” s) !l;;

in b.ml I am appending one item to global ref list l

let append l a =
l := (a)::!l

let resolveAndPacLearnSpec (features : (('a -> bool) * 'c) list) (l:int list ref) =

let res = append l 2 in

print in a.ml should print 2;2 as resolveAndPacLearnSpec is called twice but I always get 2

How should I retain items in list l? Any help will be much appreciated.


#2

Can you upload a working example? That will really help with debugging.

The main thing that stands out to me right now is that having a global mutable variable will invariably lead to bugs. State should be created by and passed around between functions that need it, not stored at the module level.


#3

So what I usually do when I write servers that utilize shared state, I make the global state is a module type and the modules using it would be functors.

module type STATE = sig 
    val global_list: int list ref
end 

module Does_Whatever (S: STATE) = struct 
   ... 
end 

let _ = 
  let state = module struct
     let global_list = [] ref 
  end in 
  
  let D = Does_Whatever(state) in 
  D.{whatever_fns}

or

module State = struct 
  let global_list = [] ref
end 

module D = Does_Whatever(State)

let _ = 
  D.whatever_fn [args]

If you’re using lwt/async.

module type SafeState = sig 
  type t

  val mu: Lwt_mutex.t  
  val set:  t -> t Lwt.t 
  val get: unit -> t Lwt.t 
end 

module type MutList = sig 
  type elt 
  val global_list : elt list ref 
end 
                        
module GlobalList (M: MutList) : SafeState with type t = M.elt list = struct 
  let mu = Lwt_mutex.create () 
                            
  let set t = 
    let f () =
      M.global_list := t;
      Lwt.return t  
    in
    
    Lwt_mutex.with_lock mu f   
                        
  let get () = 
    !M.global_list |> Lwt.return  

end

module Does_whatever(S: SafeState with type t = int list ) = struct 
  ...
end