open Core
module Int = struct
module T = struct
type t = int [@@deriving compare, sexp]
end
include T
include Comparator.Make (T)
end
type m = string Map.M(Int).t [@@deriving compare, sexp]
The least signature required for type t = int on the rwo book is [@@deriving compare, sexp_of] but the code doesn’t complain yet.
Now I need to put module Int into a functor and it cannot compile.
module Case_sexp = struct
module Int = struct
module T = struct
type t = int [@@deriving compare, sexp]
end
include T
include Comparator.Make (T)
end
type m1 = string Map.M(Int).t [@@deriving compare, sexp]
module Make (S : module type of Int) = struct
(* Error: Signature mismatch here for `S` *)
type m2 = string Map.M(S).t [@@deriving compare, sexp]
end
end
Question 1: I don’t understand here why m1 is fine but the compiler reports error at m2.
As a comparison, the code works if I follow the textbook using sexp_of instead of sexp
module Case_sexp_of = struct
module Int = struct
module T = struct
type t = int [@@deriving compare, sexp_of]
end
include T
include Comparator.Make (T)
end
type m1 = string Map.M(Int).t [@@deriving compare, sexp_of]
module Make (S : module type of Int) = struct
type m2 = string Map.M(S).t [@@deriving compare, sexp_of]
end
end
Question 2: what is the suggested way to write the signature for a core-style module with comparator a.k.a. T or with comparable. module type of Int doesn’t look so nice to me but I don’t figure out the better way.
module Case_sexp_working = struct
module My_Int = struct
module T = struct
type t = int [@@deriving compare, sexp]
end
include T
include Comparator.Make (T)
end
type m = string Map.M(My_Int).t [@@deriving compare, sexp]
module type TS = sig
include module type of My_Int
include Comparator.S with type t = t
end
module Make (S : TS) = struct
type m = string Map.M(S).t [@@deriving compare, sexp]
end
end
For the original code, the difference between Int (without signatures) and S : module type of Int may only be related to type constraints.
If I understand correctly, you are being bitten by the difference between module type of M and module type of struct include M end. The crucial difference in uses like your example is that the second form results in signatures that are strengthened by equations between the resulting signature’s types and modules and those in the original module M. So if you replace Make (S : module type of Int) by Make (S : module type of struct include Int end) in the first post’s code, it will compile.
When trying to understand this sort of issue, it is helpful to observe the signature that results from module type of using ocamlc -i or merlin, etc.
module type of Int differs from module type of struct include Int end at the inner module type for Int.T, where
module type of Int contains a signature for the type of Int.T
module type of struct include Int end has a signature of thatInt.T.
For the sake of the audience, I make a simpler code foo.ml
open Core
module Int = struct
module T = struct
type t = int [@@deriving compare, sexp]
end
include T
include Comparator.Make (T)
end
module Make (S : module type of Int) = struct
(* Error: Signature mismatch here for `S` *)
(* type m = string Map.M(S).t [@@deriving compare, sexp] *)
end
module Make1 (S : module type of struct include Int end) =
struct
type m = string Map.M(S).t [@@deriving compare, sexp]
end
module Make2 (S : module type of Int with module T := Int.T) = struct
type m = string Map.M(S).t [@@deriving compare, sexp]
end
and dump the result via ocamlfind ocamlc -package core -package ppx_jane foo.ml > debug.ml. I trimed the result type of functors for simplicity.
module Int : sig module T ... end
module Make :
functor
(S : sig
module T :
sig
type t = int
val compare : t -> t -> int
val t_of_sexp : Sexplib0.Sexp.t -> t
val sexp_of_t : t -> Sexplib0.Sexp.t
end
type t = int
val compare : t -> t -> int
val t_of_sexp : Sexplib0.Sexp.t -> t
val sexp_of_t : t -> Sexplib0.Sexp.t
type comparator_witness =
Base.Comparator.Make(T).comparator_witness
val comparator :
(T.t, comparator_witness) Core.Comparator.comparator
end)
-> sig end
module Make1 :
functor
(S : sig
module T = Int.T
type t = int
val compare : t -> t -> int
val t_of_sexp : Sexplib0.Sexp.t -> t
val sexp_of_t : t -> Sexplib0.Sexp.t
type comparator_witness =
Base.Comparator.Make(T).comparator_witness
val comparator :
(T.t, comparator_witness) Core.Comparator.comparator
end)
->
sig ... end
module Make2 :
functor
(S : sig
type t = int
val compare : t -> t -> int
val t_of_sexp : Sexplib0.Sexp.t -> t
val sexp_of_t : t -> Sexplib0.Sexp.t
type comparator_witness =
Base.Comparator.Make(Int.T).comparator_witness
val comparator :
(Int.T.t, comparator_witness) Core.Comparator.comparator
end)
->
sig ... end
It’s obvious now
In Make, the sig of S has a module type T that is standalone.
In Make1, the module T equals to Int.T.
In Make2, the code also works. It seems with module T := Int.T only reflects in the constraints but no definitions.