Consider the following code:
module type A = sig
type 'a repr
val f : string -> 'a repr
end
module type B = functor (X:A) -> sig
type 'a repr
val g : _ X.repr -> 'a repr
end
(* Example doesn't care what 'a repr really are. *)
module Example(XA:A)(XB:B) = struct
open XA
open XB(XA)
let expr () = g (f "foo")
end
where modules A
and B
describe representations and B
depends on A
. As you can see, Example
is an actual expression using those representations but it’s independent on what the actual representations really are (this is inspired by tagless-final optimizations article: http://okmij.org/ftp/tagless-final/course/optimizations.html). For example, A
could be expressions and B
could be statements.
Now, if I try to write interpreters for A
and B
for, say, printing the expression in Example
:
module AInstance = struct
type 'a repr = string
let f s = Printf.sprintf "AInstance.f(%s)" s
end
(* Here, 'a A.repr must be a string for this module to make sense.
* Is there a way to constrain it? *)
module BInstance(X:A) = struct
type 'a repr = string
let g s = Printf.sprintf "BInstance.g(%s)" s
end
let () =
let module M = Example(AInstance)(BInstance) in
print_endline (M.expr ())
the compiler complains about a signature mismatch:
File "b.ml", line 31, characters 36-45:
31 | let module M = Example(AInstance)(BInstance) in
^^^^^^^^^
Error: Signature mismatch:
Modules do not match:
functor (X : A) ->
sig type 'a repr = string val g : string -> string end
is not included in
B
At position functor (X) -> <here>
Modules do not match:
sig type 'a repr = string val g : string -> string end
is not included in
sig type 'a repr val g : 'b X.repr -> 'a repr end
At position functor (X) -> <here>
Values do not match:
val g : string -> string
is not included in
val g : 'a X.repr -> string
File "b.ml", line 8, characters 2-29: Expected declaration
File "b.ml", line 27, characters 6-7: Actual declaration
Is there a way to constrain or hint that BInstance
actually implements B
? Thanks.