Problem with sharing constraint

Here’s an example:

module type S = sig
  type msg
  val dispatch : msg -> unit
end

module A : S = struct
  type msg
  let dispatch _ = ()
end

module B = struct
  type msg
  module C = (A : S with type msg = msg)
end

This results in an error:

module C = (A : S with type msg = msg)
                   ^
Error: Signature mismatch:
       Modules do not match:
         sig type msg = A.msg val dispatch : msg -> unit end
       is not included in
         sig type msg = msg val dispatch : msg -> unit end
       Type declarations do not match:
         type msg = A.msg
       is not included in
         type msg = msg
       The type msg/1 is not equal to the type msg/2

What does this error mean? Why can’t the two abstract types get unified?

Your module declarations

module A : S = struct
  type msg
  ...
end

module B = struct
  type msg
  ...
end

creates two fresh and unrelated types named msg (or more precisely A.msg and B.msg) .
Those types are distinct and it would be a mistake to consider them equal.

If you want to make A.msg and B.msg equal, you can write:

module B = struct
  type msg = A.msg
  module C = (A : S with type msg = msg)
end
1 Like

The problem I’m trying to solve is that type msg will be provided by the user of the library, and since the library code cannot refer to the user modules unless I add functors to the api, I was hoping that there is a way to use sharing constraints to relate the msg type in all library modules. Maybe you can suggest strategies for dealing with this problem other than using functors?

If the type is provided by the user, you cannot define it yourself. Depending on the level of polymorphism needed, you can use either define functions on record of functions (or objects), or move to functors.
For instance,

type msg = { dispatch: unit -> unit }
let do_something {dispatch} = dispatch ()

or

module Do_something(X: S) = struct
  ...
end