Abstract types in modules in OCaml - module

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.

Related

expose a private type for module extension in OCaml

I'd like to extend a module but I need access to its private components. Here's an example:
nat.mli:
type t
val zero : t
val succ : t -> t
nat.ml:
type t = int
let zero = 0
let succ x = x + 1
I'd like to define a new module Ext_nat that defines a double function. I was trying to do something like this.
ext_nat.mli:
include (module type of Nat)
val double : t -> t
ext_nat.ml:
include Nat
let double x = 2 * x
It's not working as I don't have access to the representation of x in the last line.
Now that I'm thinking about this, it may not be such a good idea anyway because this would break the encapsulation of nat. So what is the best way to do this? I could define a new module nat_public where type t = int in the signature, and define nat and ext_nat with a private type t. What do you think?
You need to use with type statement. It is possible to write the code below in many different ways, but the idea is always the same.
module type NatSig =
sig
type t
val zero : t
val succ : t -> t
end
module type ExtNatSig =
sig
include NatSig
val double : t -> t
end
module ExtNat : ExtNatSig =
struct
type t = int
let zero = 0
let succ = fun x -> x + 1
let double = fun x -> x * 2
end
module Nat = (ExtNat : NatSig with type t = ExtNat.t)
let z = Nat.zero
let _ = ExtNat.double z
The problem is that as far as I remember it's impossible to achieve this behavior with your file structure: you define your module implicitly with signature in .mli file and structure itself in .ml, so you don't have enough control over you module, that's why I suggest you to reorganize your code a little bit (if it's not a problem).

Dealing with type name conflicts when include'ing two modules in OCaml

can anyone help me for the exercise 12.5 of Jason Hickey's book?
Basically, the question is how to avoid the following conflicting issue due to "include" in practice? Thanks.
# module type XSig = sig
type t
val x : t
end;;
# module A : XSig = struct
type t = int
let x = 0
end;;
# module B : XSig = struct
type t = int
let x = 1
end;;
# module C = struct
include A
include B
end;;
I don't know what the question precisely says, but regarding your code snippet, there are various very different way to interpret what you're trying to do.
What you are currently doing is sealing A and B under two abstract, incompatible signatures, then trying to mix them in a module, only to have a name conflict.
Maybe you just want to avoid the name conflict. The simplest solution, of course, is not to use the same names for both types A.t and B.t. Then you can "not include B":
module C = struct
include A
let x_b = B.x
end
The better solution is to use OCaml 3.12 destructive substitution for signatures, with type t := foo, to mask the type t from the module B:
module C = struct
include A
type u = B.t (* rename B.t into 'u' to avoid name conflict *)
include (B : XSig with type t := u) (* replaces 't' by 'u' *)
end
You may also want the types for modules A and B to be compatible. In this case you must not seal them with abstract types.
module type XSig = sig
type t
val x : t
end
module A = struct
type t = int
let x = 0
end
(* if you want to check that A can be sealed by XSig, do it here,
outside the main declaration *)
let _ = (module A : XSig)
module B = struct
type t = int
let x = 1
end
module C = struct
include (A : XSig with type t := int)
include (B : XSig with type t := int)
end
(* module C : sig
val x = int
end *)
In this example both types A.t and B.t are removed by the destructive subtitution :=. If you want your module C to have a type t, you can write either of:
module C = struct
type t = int
include (A : XSig with type t := t)
include (B : XSig with type t := t)
end
or, using non-destructive substitution (changes the type definition instead of removing it):
module C = struct
include (A : XSig with type t = int)
include (B : XSig with type t := t)
end
See the manual page for destructive substitution type type t := ... for more details, and the one on the classic with type t = ... construction for comparison.

Define a numerical polymorphic module

I would like to define a module which could support int, int64 and float. For instance,
module Matrix =
struct
type 'a t = 'a array array
(* add point-wise 2 matrices with same dimension *)
let add (m: 'a t) (n: 'a t): 'a t =
...
end
The implementation of add needs the operator plus, which is + for int, +. for float and Int64.add for int64. So I can't write anyone of them, otherwise, the type of Matrix is no more polymorphic.
Could anyone tell me how you work around this problem?
One idea I have at the moment is to make the Matrix a functor:
module type NUM_TYPE =
sig
type t
val add: t -> t -> t
end
module Matrix =
functor (Elt: NUM_TYPE)
struct
type element = Elt.t
type t = element array array
(* add point-wise 2 matrices with same dimension *)
let add (m: t) (n: t): t =
...
end
Then I have to define the following numerical modules:
module MyInt =
(struct
type t = int
let add (a: t) (b: t): t = a + b
end: NUM_TYPE)
module MyFloat = ...
module MyInt64 = ...
module MatInt = Matrix(MyInt)
module MatFloat = Matrix(MyFloat)
module MatInt64 = Matrix(MyInt64)
By this method, I find it is tedious to define MyInt, MyFloat and MyInt64, especially their own add function. Does anyone have any idea to improve this?
You could write each of those in one line like this:
module MatInt = Matrix(struct type t = int let add = (+) end)
I don't think you can do much better in OCaml (have a look at this blog post: https://ocaml.janestreet.com/?q=node/37). This would be a very nice use of typeclasses. If you're ok with using language extensions you can have a look at this project: https://github.com/jaked/deriving.

Modules and record fields

I have stumbled across a rather simple OCaml problem, but I can't seem to find an elegant solution. I'm working with functors that are applied to relatively simple modules (they usually define a type and a few functions on that type) and extend those simple modules by adding additional more complex functions, types and modules. A simplified version would be:
module type SIMPLE = sig
type t
val to_string : t -> string
val of_string : string -> t
end
module Complex = functor (S:SIMPLE) -> struct
include S
let write db id t = db # write id (S.to_string t)
let read db id = db # read id |> BatOption.map S.of_string
end
There is no need to give the simple module a name because all its functionality is present in the extended module, and the functions in the simple module are generated by camlp4 based on the type. The idiomatic use of these functors is:
module Int = Complex(struct
type t = int
end)
The problem appears when I'm working with records:
module Point2D = Complex(struct
type t = { x : int ; y : int }
end)
let (Some location) = Point2D.read db "location"
There seems to be no simple way of accessing the x and y fields defined above from outside the Point2D module, such as location.x or location.Point2D.x. How can I achieve this?
EDIT: as requested, here's a complete minimal example that displays the issue:
module type TYPE = sig
type t
val default : t
end
module Make = functor(Arg : TYPE) -> struct
include Arg
let get = function None -> default | Some x -> (x : t)
end
module Made = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
let _ = (Made.get None).a (* <-- ERROR *)
Let's look at the signature of some of the modules involved. These are the signatures generated by Ocaml, and they're principal signatures, i.e. they are the most general signatures allowed by the theory.
module Make : functor (Arg : TYPE) -> sig
type t = Arg.t
val default : t
val get : t option -> t
end
module Made : sig
type t
val default : t
val get : t option -> t
end
Notice how the equation Make(A).t = A.t is retained (so Make(A).t is a transparent type abbreviation), yet Made.t is abstract. This is because Made is the result of applying the functor to an anonymous structure, so there is no canonical name for the argument type in this case.
Record types are generative. At the level of the underlying type theory, all generative types behave like abstract types with some syntactic sugar for constructors and destructors. The only way to designate a generative type is to give its name, either the original name or one that expands to the original name via a series of type equations.
Consider what happens if you duplicate the definition of Made:
module Made1 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
module Made2 = Make(struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end)
You get two different types Made1.t and Made2.t, even though the right-hand sides of the definitions are the same. That's what generativity is all about.
Since Made.t is abstract, it's not a record type. It doesn't have any constructor. The constructors were lost when the structure argument was closed, for a lack of a name.
It so happens that with records, one often wants the syntactic sugar but not the generativity. But Ocaml doesn't have any structural record types. It has generative record types, and it has objects, which from a type theoretical view subsume records but in practice can be a little more work to use and have a small performance penalty.
module Made_object = Make(struct
type t = <a : int>
let default = object method a = 0 end
end)
Or, if you want to keep the same type definition, you need to provide a name for the type and its constructors, which means naming the structure.
module A = struct
type t = {a : int}
let default = { a = 0 } (* <-- Generated by camlp4 based on type t above *)
end
module MadeA = Make(A)
Note that if you build Make(A) twice, you get the same types all around.
module MadeA1 = Make(A)
module MadeA2 = Make(A)
(Ok, this isn't remarkable here, but you'd still get the same abstract types in MadeA1 and MakeA2, unlike the Made1 and Made2 case above. That's because now there's a name for these types: MadeA1.t = Make(A).t.)
First of all, in your last code sample, last line, you probably mean .a rather than .x.
The problem with your code is that, with the way you define your Make functor, the type t is abstract in Made: indeed, the functors use the TYPE signature which seals {a : int} as an abstract type.
The following design circumvent the issue, but, well, its a different design.
module type TYPE = sig
type t
val default : t
end
module Extend = functor(Arg : TYPE) -> struct
open Arg
let get = function None -> default | Some x -> (x : t)
end
module T = struct
type t = {a : int}
let default = { a = 0 }
end
module Made = struct
include T
include Extend(T)
end
let _ = Made.((get None).a)
The problem is that OCaml doesn't have a name to refer to the qualified components of the type t (in this case a record, but the same problem would be present with normal variants) outside Made. Naming the unnamed solves the problem:
module F = struct
type t = {a : int}
let default = { a = 0 }
end
module Made = Make(F)
let _ = (Made.get None).F.a (* <-- WORKS *)
You can also declare explicitly the type outside the functorial application:
type rcd = {a : int}
module Made = Make(struct
type t = rcd
let default = { a = 0 }
end)
let _ = (Made.get None).a (* <-- WORKS *)

Relaxing type checking when using 'with type' construction in modules

I have defined two module types and two modules
module type FOO = sig type e end
module type BAR = sig type t end
module Foo : FOO = struct type e = int end
module Bar : BAR = struct type t = int end
Then I define a functor as
module Fun (F:FOO) (B:BAR with type t = F.e) = struct type x = string end
(this is a toy example, please ignore the fact that F and B are not used by the functor)
Now, if I define the module
module Bla = Fun (Foo) (Bar)
I get
Error: Signature mismatch:
Modules do not match:
sig type t = Bar.t end
is not included in
sig type t = Foo.e end
Type declarations do not match:
type t = Bar.t
is not included in
type t = Foo.e
Although both Bar.t and Foo.e are defined as int OCaml considers Bar.t and Foo.e to be different. That's just the way the typing system works and it makes sense to consider these two types different in general (c.f. last paragraph of Functors and Type Abstraction).
Question: Sometimes I may want this to pass type checking because for my purposes they can be considered equal. Is there a way to relax this?
Using gasche's suggestion of removing coercion, the above code can be written as
module type FOO = sig type e end
module type BAR = sig type t end
module Foo = struct type e = int end
module Bar = struct type t = int end
module Fun (F : FOO with type e=int) (B : BAR with type t = int) = struct type x = F.e * B.t end
module Bla = Fun (Foo) (Bar)
which compiles fine. Strangely, I get
# let f x : Bla.x = (x,x);;
val f : Foo.e -> Bla.x = <fun>
Question: why does it infer that x is Foo.e? It could as well be Bar.t?
The problem is how you define Foo and Bar : module Foo : FOO = .... By imposing this signature here, you "seal" the module and make the type abstract. It cannot be reverted. You should remove the : FOO coercion here, and use it later when you need the abstraction. You could also use module Foo : (FOO with type e = int) = ....
I'm not sure how the printer chooses amongst equal types, but in this case you can cause it to print a different name by explicitly annotating your function argument:
# let f (x:Bar.t) : Bla.x = (x,x);;
val f : Bar.t -> Bla.x = <fun>