I’m currently learning about functors in my university course on OCaml.
Right now, I’m having trouble using an instance of a functor, here’s my code ensemble.ml:
(* Signature ELT (element) *)
module type ELT = sig
type t
val compare : t -> t -> int
end
(* Signature S (set) *)
module type S = sig
type elt
type t
val vide : t
val element_de : elt -> t -> bool
val ajout : t -> elt -> t
end
(* Implementation A of signature ELT *)
module A : ELT = struct
type t = int
let compare x y = x - y
end
(* Functor MakeL with signature argument E of type ELT that produces a module S with type elt = E.t (same element type) *)
module MakeL(E : ELT) : S with type elt = E.t
=
struct
type elt = E.t
type t = elt list
let vide = []
let element_de elt t = List.exists (fun e -> (compare e elt) = 0) t
let ajout t elt = elt :: t
end
module Test = MakeL(A)
let l1 = [1;4;5]
let l2 = Test.ajout l1 6
When I compile it with ocamlc -c, it outputs
File "ensemble.ml", line 58, characters 20-22:
58 | let l2 = Test.ajout l1 6
^^
Error: This expression has type int list
but an expression was expected of type Test.t = MakeL(A).t
Am I missing something? I thought l1 is already of type A.t list since A.t = int as implemented.
On a further note, I want to know how to access these fonctors from another file. Let’s say I put the Test module in test.ml. I get the following error:
File "test.ml", line 2, characters 20-21:
2 | module Test = MakeL(A)
^
Error: Unbound module A
How do functors and modules differ from functions when they can’t be accessed from other source files in the same directory when the latter can?
One more thing, once you start organizing your code into multiple files, you will save yourself a lot of hassle by using dune to build the project instead of manually typing in ocamlc commands.
I’ve copied your code into TryOCaml, see the link here.
If you click on the ‘Eval code’ button, you’ll see what the types are for the various modules.
In particular, you’ll see that module A has type ELT, and if you look at the definition of ELT you’ll see that it never mentions the int type. So the link between A.t and int has been hidden. As @orbitz noted, this is because module A : ELT in the definition explicitly tells the compiler to forget the actual type and use the abstract ELT type instead. If you remove the : ELT annotation you’ll get the link between the types back.
The same happens again for the link between Test.t and int list. If you look at your functor MakeL, here is the type printed in the toplevel:
module MakeL :
functor (E : ELT) ->
sig
type elt = E.t
type t
val vide : t
val element_de : elt -> t -> bool
val ajout : t -> elt -> t
end
You can see that it never mentions any lists, so even after fixing the issue with A you’ll get the same error about the type of l1.
Here you have two solutions: either you choose to expose the details of your implementation, to allow the rest of the code to use lists and sets equivalently, or you keep the details hidden and use the functions from your module to create the sets.
Concretely, the first option means adding an extra constraint on the return type of your functor: module MakeL(E : ELT) : S with type elt = E.t and type t = E.t list = ...
The second option means updating the definition of l1: let l1 = Test.ajout (Test.ajout (Test.ajout Test.vide 1) 4) 5
The second option is more idiomatic, but the first option makes it easier to see the values in the toplevel, so it might be interesting for you to experiment with both ideas.
Also, on an unrelated subject, please note that let compare x y = x - y is dangerous, as it will return wrong results in case of overflow (for instance, compare max_int (-1) will return a negative number, implying that max_int is smaller than -1).