Destructive module substitution: changed in 4.07

Hello all!

The following code fragment compiles with OCaml 4.02.3 to 4.06.0 but not with 4.07:

module Original = struct
  module X = struct
    let x = 42
  end
end

module Intermediate = struct
  include Original
end

module Final = struct
  module X = struct
    let x = 43
  end

  include (Intermediate: module type of Intermediate with module X := X)
end

With 4.07, it fails with this error message:

Error: In this `with' constraint, the new definition of X
       does not match its original definition in the constrained signature:
       Modules do not match:
         (module X)
       is not included in
         (module Original.X)

If you remove the Intermediate module (and include (Original: module type of Original with module X := X), it does compile with all versions I tried.

Questions:

  • is it expected to compile? (i.e. was it a bug in previous versions or is it a regression in 4.07?)
  • is there a workaround to make it work with 4.07

Thanks!

This is an intended changes in 4.07, module type of does no longer remove module aliases information. You can get back the old behavior by adding a [@remove_aliases] attribute:

module type of Intermediate[@remove_aliases]
1 Like

Awesome answer, thanks! I’ll add this annotation in my real-world use-case.

If you don’t want to use attributes to change the module system semantics, you can remove a submodule by using itself, e.g.:

  include (Intermediate: module type of Intermediate with module X := Intermediate.X)

This should always work, too. The only downside is that your new module X implementation won’t be checked for compatibility with the old one. In that case you can always constrain it explicitly, e.g.:

  module X : module type of Intermediate.X = struct
    let x = 43
  end
1 Like