What's the runtime representation of first-class modules?

As in something like

let f (module M : MOD) =
  M.f "neat"

Just trying to get an intuition for what this is like at runtime.

2 Likes

same as that of a record. IDK how to safely demonstrate that, but here:

module type R = sig
  val a : int
  val b : int
  val c : int
end
  
type r = {
  a : int;
  b : int;
  c : int;
}
  
let a = { a = 1; b = 2; c = 3 }
let b = (module struct let a = 1 let b = 2 let c = 3 end : R)

let t = a = Obj.magic b
let {a; b; c} = Obj.magic b

(*
val t : bool = true
val a : int = 1
val b : int = 2
val c : int = 3
*)
3 Likes

Oh nice, thanks! So since the module that is passed in can be a superset of the signature (MOD in my example), is there like an ad-hoc record that’s generated at the call site, or something along those lines?

I don’t know the details of that! I do assume in my programming that packing a module involves a creation of a new block in memory.

Yes, if the actual type of the module and the signature it’s used at are different then the compiler will generate an allocation that copies the relevant fields from the input module into a structure that fits the signature. There are a few cases when the allocation can be elided (when the signatures are similar enough that the input modules “fits” the expected signature), but in general you should expect an allocation.

2 Likes

Perfect, I was hoping it would be something relatively straightforward like that. Thanks, both of you!