-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Behavior of 'module type of' w.r.t. module aliases #6307
Description
Original bug ID: 6307
Reporter: @alainfrisch
Assigned to: @garrigue
Status: closed (set by @xavierleroy on 2015-12-11T18:25:46Z)
Resolution: fixed
Priority: normal
Severity: minor
Fixed in version: 4.02.0+dev
Category: typing
Tags: patch
Related to: #6325
Monitored by: @gasche @diml @jmeber "Julien Signoles" @yallop @hcarty
Bug description
The introduction of module aliases can break code which uses 'module type of'. Example:
=================================================================== module A1 = struct end module A2 = struct end module L1 = struct module X = A1 end module L2 = struct module X = A2 end module F (L : (module type of L1)) = struct end module F1 = F(L1) module F2 = F(L2) ===================================================================
Previously, the type of F's argument was "sig module X : sig end end", and now it is "sig module X = A1 end", making L2 incompatible with it.
A discussion about what to do started on caml-devel. Here is Jacques' reply to my email:
What should we do about it?
An easy fix when the submodule contains no abstract types is to add an annotation on module X:
module L1 = struct
module X : module type of A1 = A1
end
I'm tempted to argue in favor of generalizing the special treatment of 'module type of' w.r.t. module aliases to submodules to keep backward compatibility. This seems also more coherent with the treatment of 'module type of' w.r.t. type aliases.
I'm not sure of what you mean by your second sentence.
'module type of' does keep type aliases.
The only specific behavior is that it does not strengthen the types, i.e. it does not add aliases to abstract types.
This is not what happens here for the module aliases: the alias comes from the definition in L1, not from module type of.
So this would not just mean not adding aliases, but actively removing them from the signature, recursively.
This said I agree that the choice not to strengthen abstract types had your use case in mind.
Since the problem we see here is again specific to this use case, it could be reasonable to solve it in the same direction.
However, there is also a new problem here: if we do not leave the alias by default, we need a way to reintroduce it.
Currently "(module type of L1) with module X = A1" does not reintroduce an alias.
I suppose it should introduce one, so this is rather a bug.
What do you think?
I'm not sure what is the best solution.
In particular, experience has shown that not strengthening 'module type of' was actually a bad idea.
In at least 95% of cases you want the strengthened version, and doing the strengthening by hand is a pain.
The use case you describe here seems pretty rare, to the point I was even wondering wether we should not revert to the strengthening semantics. But, as rare as it is, you demonstrated that it happens in practice, so maybe we need two versions of 'module type of'.
I suppose it also relates to the expected semantics of 'module type of'.
Is it just something which looks up a module type in the environment,
or is it something more meta-level, which behaves like syntactically copying the module type.
#6305 is an example of that: there the intuition is that 'module type of A' should not introduce a dependency on A, since we do not access any internal part of A, but this would require some specific treatment.
#6305