I have very simple signature and module in OCaml:
module type S = sig
type t
val y : t
end;;
and
module M2 : S = struct
type t = int
let x = 1
let y = x+2
end;;
I cannot use construction like
M2.y
to get 3 unless i specify the module as
module M2 : S with type t = int = struct ...
Why is it so? There already is statement, that type t = int
The concrete, int value for M2.y is indeed not available because the following two conditions are met:
the type of y is abstract in the signature S
(there is no type t = ... there)
the module M2 is made opaque with respect to the signature S
(in other words, it is restricted to the signature S via the notation : S)
As a result, you indeed obtain:
let test = M2.y ;;
(* val test : M2.t = <abstr> *)
As suggested by the keyword <abstr>, this is related to the notion of abstract type. This notion is a very strong feature enforced by OCaml's typing rules, which prevents any user of a module having signature S to inspect the concrete content of one such abstract type. As a result, this property is very useful to implement so-called abstract data types (ADT) in OCaml, by carefully separating the implementation and the signature of the ADT.
If any of the two conditions above is missing, the type won't be abstract anymore and the concrete value of y will show up.
More precisely:
If the type t is made concrete, you obtain:
module type S = sig
type t = int
val y : t
end
module M2 : S = struct
type t = int
let x = 1
let y = x+2
end
let test = M2.y ;;
(* val test : M2.t = 3 *)
But in practice this is not very interesting because you lose generality. However, a somewhat more interesting approach consists in adding an "evaluator" or a "pretty-printer" function to the signature, such as the value int_of_t below:
module type S = sig
type t
val y : t
val int_of_t : t -> int
end
module M2 : S = struct
type t = int
let x = 1
let y = x+2
let int_of_t x = x
end
let test = M2.(int_of_t y) ;;
(* val test : int = 3 *)
Otherwise, if the module M2 is made transparent, you obtain:
module type S = sig
type t
val y : t
end
module M2 (* :S *) = struct
type t = int
let x = 1
let y = x+2
end
let test = M2.y ;;
(* val test : int = 3 *)
Finally, it may be helpful to note that beyond that feature of abstract types, OCaml also provides a feature of private types that can be viewed as a trade-off between concrete and abstract types used in a modular development. For more details on this notion, see for example Chap. 8 of Caml ref man.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With