Let's suppose we have a module defined like this:
module type LIST_ZIPPER =
sig
type 'a zipper
exception First
exception Last
exception Empty
val empty : 'a zipper
(* returns the sublist starting at focus position*)
val get : 'a zipper -> 'a list
(* add/remove under focus operation *)
val insert : 'a zipper -> 'a -> 'a zipper
val remove : 'a zipper -> 'a zipper option
val remove_exn : 'a zipper -> 'a zipper
(* focus-moving operation *)
val previous : 'a zipper -> 'a zipper
val next : 'a zipper -> 'a zipper
end
module ListZipper2 : LIST_ZIPPER =
struct
type 'a zipper = LZ of 'a list * 'a list
exception First
exception Last
exception Empty
let empty : 'a zipper = LZ ([],[])
let get (LZ (_,l) : 'a zipper) : 'a list = l
let insert (LZ (l, r)) a = LZ (l,a::r)
let remove lz =
match lz with
| LZ (l, h::t) -> Some (LZ (l,t))
| LZ (_,[]) -> None
let remove_exn (lz : 'a zipper) : 'a zipper =
match remove lz with
| Some lz -> lz
| None -> raise Empty
let previous lz =
match lz with
| LZ (h::t, l) -> LZ (t, h::l)
| LZ ([],_) -> raise First
let next lz =
match lz with
| LZ (l, h::t) -> LZ (h::l,t)
| LZ (_,[]) -> raise Last
end
and I want to to create a zipper like this:
open ListZipper2
let (zip : ListZipper2.zipper) = LZ ([1;2;3], [4;5])
and it doesn't work, what is the proper way? I can access operations defined such as ListZipper2.insert
, but if I can't create a zipper, then what is the benefit of module in this case?
Your module type LIST_ZIPPER
most probably defines 'a zipper
as an abstract type:
module type LIST_ZIPPER = sig
type 'a zipper
...
end
Once you have restricted an implementation to this module type with
module ListZipper2 : LIST_ZIPPER = struct
...
end
you can no longer peek inside the implementation detail of the module.
This is by design since it enforces that the rest of the code will work with any LIST_ZIPPER
independently of its contingent implementation.
Note that this requires the LIST_ZIPPER
module type to support enough operations for subsequent users to use the zipper
. If you defined the LIST_ZIPPER
module type yourself, it may happen that the module resulting
from the signature constraint is unusable. For instance, with
module type group = sig
type t
val (+): t -> t -> t
end
module Int: group = struct
type t
let (+) = (+)
end
the resulting module is unusable because there is no way to construct a value of type t exposed in the module type group
.