OCaml First-Class Modules: Runtime Overhead and Type Identity
0 reputation · 03 Aug 2022, 13:10 UTC
0 reputation · 03 Aug 2022, 13:10 UTC
OCaml's module system provides a mechanism for strong encapsulation through signatures and structures. While static module instantiation is the standard for type-safe abstraction, first-class modules allow modules to be treated as values, enabling dynamic selection of implementations at runtime.
There is a design trade-off between the flexibility of first-class modules and the performance characteristics of static modules. Specifically, the transition from a static module to a first-class module may introduce runtime overhead and impact how the compiler handles type identity across abstraction boundaries.
34775 reputation · 03 Aug 2022, 20:55 UTC
When a function is called through a first‑class module, the call site must first load the module value (a record or closure) from the heap, then fetch the function pointer from that record before jumping to the code. In native code with optimizations enabled, this translates to one extra pointer load and a small branch, typically a few nanoseconds per call in microbenchmarks. Static module calls, by contrast, are resolved at compile time to a direct symbol reference, allowing the compiler to inline the function or eliminate the call entirely. The measurable difference appears only when the module is packaged as a value at runtime; if the compiler can see the module statically, it can remove the indirection.
Packaging a module as a first‑class value erases its concrete type to an existential (module Sig). The compiler then treats the value as an opaque type, preventing optimisations that rely on knowing the exact structure, such as specialization, inlining across functor boundaries, or type‑based constant propagation. Consequently, type identity across abstraction boundaries is lost unless the programmer explicitly re‑exposes the concrete type. This can be achieved by ascribing the packed module with a concrete type (e.g., (module Sig with type t = int) : (module Sig)) or by using GADTs that carry a type‑equality witness. Without such annotations, the compiler must assume the most general abstract signature and cannot perform optimisations that depend on the concrete implementation.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.