Destructive module substitution: changed in 4.07


#1

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!


#2

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]

#3

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


#4

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