Saving a module in a reference

Hi,

I am new to Ocaml and I am wondering whether there is a way to store a module in a reference?

Specifically, I would like to create a module using a functor at the beginning of my program then use it aftewards (or even in other modules). The behavior would depend on some parameter (a user entry for instance). Something like :

Module MyFunctor = Functor ….

myRef = ref MyFunctor(RightBehaviour) (*default*)

If user_input = ‘right’ then

myRef := MyFunctor(LeftBehaviour)

End;

!myRef.DoSomething

Any ideas?

You’re probably looking for first-class modules. Here is the corresponding section of the manual: https://ocaml.org/manual/5.2/firstclassmodules.html

Although your code example doesn’t look like OCaml code at all. If you’re using an alternate syntax, it’s possible that first-class modules are not supported.

We can reference a module, but the module type can’t be inferred with ref. This means you have to declare a module type:

module type DOERS = sig
  val doSomething : int -> unit
end

module A = struct
  let doSomething = print_int
end

module MyFunctor (M : DOERS) =
  struct
    let doSomething v =
      print_string "value=";
      M.doSomething v;
      print_string "\n"
  end

let myRef = ref (module A : DOERS)

let () =
  if true then
    myRef := (module MyFunctor(A));
  let module M = (val !myRef) in
    M.doSomething 42

Note the (module …) and (val …) syntax which converts a module into a value which can be referenced, passed as an argument to a function, etc… and vice-versa.

Beware of the case (if not If…) and end closes a begin. (Not required with an if and a single statement).

Thank you for your answer.
Now however, I have another problem.
If I want doSomething to print integers and strings (depending on the parameter of the functor) the compiler seams to be confused on the type of the parameter of doSomething since it does know it before runtime.
Specifically if I change your code this way :

module type DOERS = sig
  type t
  val doSomething : t -> unit
end

module A = struct
  type t=int
  let doSomething = print_int
end

module B = struct
  type t=string
  let doSomething = print_endline
end

module MyFunctor (M : DOERS) =
  struct
    type t = M.t
    let doSomething v =
      print_string "value=";
      M.doSomething v;
      print_string "\n"
  end

let myRef = ref (module A : DOERS)

let () =
  if true then
    myRef := (module MyFunctor(A));
  let module M = (val !myRef) in
    M.doSomething 42

I have an error :
31 | M.doSomething 42
^^
Error: This expression has type int but an expression was expected of type M.t

Of course it works if I do

let module Yy = MyFunctor(A) in
      Yy.doSomething 42;

How can I fix that?

If you want your program to have a single reference (myRef) point to a module which deals with int or a string depending of a condition known at the compile time, you need a dynamic typing. But OCaml has a static typing. I guess you can’t do what you want directly.

You may use algebraic data type (type var = Int of int | String of string …) but since the type can’t be checked at the compile time, you have to use pattern matching and check the if you can deal with an int or a string….

In addition to @Frederic_Loyer’s answer, here are two other possible solutions:

  • Adapt the definition of myRef (if this reference only stores modules manipulating integers):
let myRef = ref (module A : DOERS with type t = int)
  • Add a function converting int to the type t to DOERS:
module type DOERS = sig
  type t
  val from_int : int -> t
  val doSomething : t -> unit
end
...
    M.doSomething (M.from_int 42)

Thanks both for your answer.
I was wondering if “with type M.t=int” would help the compiler induce the correct type. But I am not sure how to use it.

Do you think it could help?

The code fix

let myRef = ref (module A : DOERS with type t = int)

is enough to solve your error.

But it will prevent you to affect to myRef a module where t is not int. Since I don’t know why you want the type t to be variable, I don’t know if it is an issue.

As I have said, OCaml has a static type system, then all types must be known at compile time. (And with the type erasure, the OCaml system prevents the definition of an introspection library or operator, well, Obj is an introspection library but very limited).

Let’s suppose you want to type:

  if condition then
    M.doSomething 42
  else
    M.doSomething "hello"

OCaml will infer for M.doSomething, two incompatible types. This can’t work whatever the M module definition.

What can be done is something like this:

module type DOERS = sig
   val doSomething : unit -> unit
end

let refM =
  let module A = struct
     let v = 42
     let doSomething () = print_int v
  end in
    ref (module A : DOERS)

let () = if true then
  let module B = struct
     let v = "hello"
     let doSomething () = print_string v
  end in
    refM := (module B)

let () = let module M = (val !refM) in
  M.doSomething ()

Here refM has simply the type DOERS… then you can affect any module (based on int, string, whatever). But you can only use fixed type functions (here doSomething () the internal integer is not available from refM)

The proposal:

module type DOERS = sig
  type t
  val from_int : int -> t
  val doSomething : t -> unit
end
...
    M.doSomething (M.from_int 42)

is a bit similar: you have to use fixed type fonction and the composition of doSomething and from_int has type int -> unit whatever the t type. With this version, you can access the intermediate value, but with an opaque type: you won’t be able to do much with it, and here a doSomethingWithInt : int -> () would be simpler.

But I find it hard to guess what you really want to do.

Ok. So if I get it right, unlike other dynamically typed languages (such as Java and Smalltalk), it is not possible to write a program that adapts their behavior at runtime. That is why, for instance, it is not possible to write a universal printer (a single function that can take a string or an integer as a parameter and print it)?

Am I right?

Types in OCaml gives a static/compile-time description of the behaviours of your program and functions. In other words, by definition types cannot change between different runtime execution. This also implies that runtime variables cannot peek at the compile-time description of their own behaviour to adjust their behaviour. This does not mean that a program cannot adapt its behaviour at runtime however.

Overall, your issue looks to me like an XY problem: putting modules into a reference is kind of doable but leans quite closely to the advanced edge of the language and is not idiomatic.

It would be probably simpler in the long run to restructure your code to avoid this pattern. For instance, you could make the generated module an explicit argument of functions and modules using it rather than using global state to pass it implicitly as an argument.

Having a general printer would be useful with variable which may contains different types depending of the execution… but this can’t happen with OCaml.

If you type let a = 42, you know a is an integer, then you are able to type afterward doSomethingWithInt a.

No problem.

And let a = if condition then 42 else "hello" will not compile.

But you can have genericity:

let doSomething a helper =
  print_string "=";
  helper a;
  print_string "=\n";;
val doSomething : 'a -> ('a -> 'b) -> unit = <fun>

Here doSomething can work with any type of argument, but in order to use them efficiently, would need a compatible helper function. Functors can help making a module specialized for a type. Typically module IntSet = Set.Make(Int)… here the module Int provides the type and the helper function. But it will provide roughly the same end result: Set is generic, but creates IntSet which is specialized. You can’t have sets of anything.

That depends on what you mean. It is perfectly possible to write code which has behaviour similar to that of, say, virtual functions in C++.

A module which implement a type module signature can be seen as a set of virtual methods. But in the main program all types are known precisely.

I had proposed an example of a module which includes its value, then the doSomething can be call with a unit argument. This can achieve some polymorphic feature.

Quite so. And you can do something similar with closures.

Thank you all for your answers.

Maybe if I describe a bit what I am trying to do it will be clearer.

Actually, I would like to write a program that may invoque two different tools depending on the file type passed in parameter (.t1 → tool1, .t2->tool2).
The two tools have different data structures.
So, my idea was (more or less inspired by my previous experience with OO) create a module depending on the target tool, put it in a reference then use this reference to access the desired “behavior”. Something like :


let myRef = ref (module A : DOERS)

let () =
  if (someConditionOfTheTypeOfDocument) then
    myRef := (module MyFunctor(A));
  let module M = (val !myRef) in
....

And later if a module needs a computation implemented in MyFunctor :

Main.M.doSomething whateverStructureIReadFromExternalTool

But of course here, M is not accessible from outside the () function but this is just the idea.

Then you have to abstract out of your two different tools a common uniformly typed module interface from which you can construct your first class modules, just as you would in designing the interface of your base class for your OO solution.

It seems complicated for something like this

  let () = if (isTypeA) then
     let s = readDocumentA file in
     processDocA s
  else
     let s = readDocumentB file in
     processDocB s

S is an OCaml structure whose the actual type depends of the document.

1 Like

I suspect that a more natural approach would be to wrap the program logic in a functor which can then operate on a module passed as a parameter. Consider:

module Program (M: S) = struct
  let () =
    M.doSomething whateverStructureIReadFromExternalTool
end

let () =
  if (someConditionOfTheTypeOfDocument) then
    let module M = Program (A) in ()
  else
    let module M = Program (B) in ()

If whateverStryctureReadFromExternalTool may have different OCaml type (dependent of the document type), we would have to write.

module Program (M: S) = struct
  let () =
    let str = M.readFromExternal () in
    M.doSomething str
end

let () =
  if (someConditionOfTheTypeOfDocument) then
    let module M = Program (A) in ()
  else
    let module M = Program (B) in ()

The type of str will typically be in the S signature and instantiated by A and B.