Reputation: 1032
When creating a library from multiple modules, I cannot find a nice way to do proper information hiding to the user of the library (external interface) while being able to access everything I need on the internal interface.
To be more specific, I have two modules (Files a.ml[i] and b.ml[i]). In A, I define some type t, thats internals I don't want to hide from the user (external interface).
module A : sig
type t
end
module A = struct
type t = float
end
In module B, I then want to use the secret type of A.t
.
module B : sig
create_a : float -> A.t
end
module B = struct
create_a x = x
end
This of course does not compile, because the compilation unit of B does not know the type of A.t
.
Solutions I know, but don't like:
create_a
to module A
A.t
to B
and cheat the type checker with some external cheat : `a -> `b = "%identity"
Is there some other way to know the type of A.t
in B
without leaking this information to the library's interface?
Upvotes: 5
Views: 864
Reputation: 35280
As always an extra layer of indirection can solve this problem. Define a module Lib
that will specify an external interface, e.g.,
module Lib : sig
module A : sig
type t
(* public interface *)
end
module B : sig
type t
(* public interface *)
end = struct
module A = A
module B = B
end
If you don't want to repeat yourself and write module signatures twice, then you can define them once in a module sigs.ml
:
module Sigs = struct
module type A = sig
type t
(* public interface *)
end
(* alternatively, you can move it into sigs_priv.ml *)
module type A_private = sig
include A
val create_a : float -> t
end
...
end
Finally, make sure that you're not installing interfaces (the .cmi
files),
during your installation step, so that users can't bypass your abstraction. If your're using oasis, then it is simple: just make all your modules internal, except the module Lib
, i.e., specify them with InternalModules
field.
Upvotes: 7