Question about variables in module behavior

I’m trying to understand whether or not this is intended behavior:

I noticed that values in modules aren’t necessarily scoped to their own module. For example, if I call make twice in the module below, the state array will populate for both modules. i intuitively expected for the arrays to be isolated from each other.

module MyModule = {
  type t = array<string>
  let state: t = []

  let make = (foo: string): t => {
    state->Array.push(foo)
    state
  }
}

let a1 = MyModule.make("a")
let _a2 = MyModule.make("b")

Console.log(a1) // ["a", "b"]

This is also an issue for refs. This seems like a bug, but I doubt myself because Melange with Reason has the same behavior when I tested it there as well – fwiw modules in OCaml do not behave in this way

I understand that I’m probably doing something weird here. I imagine it’s better to have the make function create an array in its own scope to return then other functions could be created that accept arguments of type t rather than interacting with state internal to the module. The behavior from the example in the main post still seems sort of weird tho :slight_smile:

The reason you are seeing this is because of JavaScript. Here’s the compiled JS from what you wrote:

// Generated by ReScript, PLEASE EDIT WITH CARE

let state = [];

function make(foo) {
  state.push(foo);
  return state;
}

let MyModule = {
  state: state,
  make: make
};

let a1 = make("a");

let _a2 = make("b");

console.log(a1);

export {
  MyModule,
  a1,
  _a2,
}
/* a1 Not a pure module */

OCaml behaves differently because Modules are represented differently in the runtime. ReScript and Melange are compiling to JS, so you will see the same results.

If you want state to be unique to each call for make you would need to make sure it’s part of the closure for the make function, which keeps it isolated to only that function.

module MyModule = {
  type t = array<string>

  let make = (foo: string): t => {
    let state: t = []
    state->Array.push(foo)
    state
  }
}

Got it; sounds like expected behavior, thanks!

you would actually get the same behavior in ocaml, go to ocaml playground and copy paste this:

module MyModule = struct
  let state = ref([])

  let make (foo: string)  = 
    state := foo :: !state;
    state
end

let a1 = MyModule.make "a";;
let a2 = MyModule.make "b";;
Printf.printf "a1 = a2 ? %b" (!a1 = !a2);;
(* prints a1 = a2 ? true *)
2 Likes

Yikes, I must have done something weird to get the results I did – I’m a complete amateur when it comes to the language. Thanks for pointing that out!

2 Likes