I am new to OCaml and I am struggling a bit with understanding how module types work.
module type I = sig
type t
end
module EQ (M : I) = struct
let equal (x : M.t) (y : M.t) = x = y
end
(* Explicitly type declaration *)
module A : I = struct
type t = string
end
module EStr1 = EQ (A)
(* Error: This expression has type string but an expression was expected of type
A.t *)
let _ = EStr1.equal "1" "0" |> string_of_bool |> print_endline
(* No type declaration *)
module B = struct
type t = string
end
module EStr2 = EQ (B)
(* OK. Outputs false *)
let _ = EStr2.equal "1" "0" |> string_of_bool |> print_endline
In the code above i declared module type I
, module A
with explicit module type declaration and module B
without module type declaration. EQ
is functor that receives module of type I
and returns module with equal
method.
Example with module A
result in compiling error, while another works as I expected. What is semantic difference between this examples?
The output from the toplevel interpreter is instructive.
# module type I = sig
type t
end
module EQ (M : I) = struct
let equal (x : M.t) (y : M.t) = x = y
end
(* Explicitly type declaration *)
module A : I = struct
type t = string
end
module EStr1 = EQ (A);;
module type I = sig type t end
module EQ : functor (M : I) -> sig val equal : M.t -> M.t -> bool end
module A : I
module EStr1 : sig val equal : A.t -> A.t -> bool end
Note the signature for the EStr1
module. EStr1.equal
has type A.t -> A.t -> bool
. Not type string -> string -> bool
.
Applying the I
signature to A
has restricted us from knowing what A.t
is. It is an abstract type. You haven't done that with B
, so EStr2.equal
does have type string -> string -> bool
.
You can also explicitly expose this type information.
# module A : I with type t = string = struct
type t = string
end;;
module A : sig type t = string end
# module C = EQ (A);;
module C : sig val equal : string -> string -> bool end
Once you understand how abstract types work, they are very useful.