Mutually recursive module and functor in OCaml - ocaml

I have defined an interface A to be used by several functors, and notably by MyFunctor :
module type A = sig
val basic_func: ...
val complex_func: ...
end
module MyFunctor :
functor (SomeA : A) ->
struct
...
let complex_impl params =
...
(* Here I call 'basic_func' from SomeA *)
SomeA.basic_func ...
...
end
Now I want to define a module B with implements the interface A. In particular, the implementation of complex_func should use basic_func through complex_impl in MyFunctor :
module B = struct
let basic_func = ...
let complex_func ... =
let module Impl = MyFunctor(B) in
Impl.complex_impl ...
end
However, this code doesn't compile as B is not fully declared in the context of MyFunctor(B). Obviously B depends on MyFunctor(B), which itself depends on B, so I tried to use the rec keyword on module B, but it didn't work out.
So, is it possible to do something like this ? It would be useful as I have several modules B_1, ..., B_n that use the same implementation of B_k.complex_func in terms of B_k.basic_func.
Or is there a better pattern for my problem ? I know that I can declare complex_impl as a regular function taking basic_func as a parameter, without using a functor at all :
let complex_impl basic_func params =
...
basic_func ...
...
But in my case complex_impl uses many basic functions of A, and I think that the paradigm of functors is clearer and less error-prone.
Edit : I followed this answer, but in fact, A uses some type t that is specialized in B :
module type A = sig
type t
val basic_func: t -> unit
val complex_func: t -> unit
end
module MyFunctor :
functor (SomeA : A) ->
struct
let complex_impl (x : SomeA.t) =
SomeA.basic_func x
...
end
module rec B : A = struct
type t = int
val basic_func (x : t) = ...
val complex_func (x : t) =
let module Impl = MyFunctor(B) in
Impl.complex_impl x
end
And now I get the error (for x at line Impl.complex_impl x) :
This expression has type t = int but an expression was expected of type B.t
Edit 2 : I solved this second problem with the following code :
module rec B :
A with type t = int
= struct
type t = int
...
end

You can use recursive modules just like you'd write recursive let bindings
module type A = sig
val basic_func : unit -> int
val complex_func : unit -> int
end
module MyFunctor =
functor (SomeA : A) ->
struct
let complex_impl = SomeA.basic_func
end
module rec B : A = struct
let basic_func () = 0
let complex_func () =
let module Impl = MyFunctor(B) in
Impl.complex_impl ()
end
Note (a) the module rec bit in the definition of B and (b) that I am required to provide a module signature for a recursive module definition.
# B.basic_func ();;
- : int = 0
# B.complex_func ();;
- : int = 0
There's a small caveat, however, in that this only works because the signature A has only values which are function types. It is thus known as a "safe module". If basic_func and complex_func were values instead of function types then it would fail upon compilation
Error: Cannot safely evaluate the definition
of the recursively-defined module B

Related

How to apply [##deriving show] to a type from module parameter of my functor?

I have a functor that takes a Set type like:
module type MySet = functor (S : Set.S) -> sig
val my_method : S.t -> S.elt -> S.elt list option
end
module MySet_Make : MySet = functor (S : Set.S) -> struct
let my_method set el = Some [el] (* whatever *)
end
module IntSet = Set.Make(Int)
module MyIntSet = MySet_Make(IntSet)
S.elt is the type of elements of the set
I want to apply [##deriving show] (from https://github.com/ocaml-ppx/ppx_deriving#plugin-show) to S.elt within my functor somehow, so that in one of my methods I can rely on having a show : S.elt -> string function available.
I feel like it must be possible but I can't work out the right syntax.
Alternatively - if there's a way to specify in the signature that the Set type S was made having elements of a "showable" type.
e.g. I can define:
module type Showable = sig
type t [##deriving show]
end
...but I can't work out how to specify that as a type constraint to elements of (S : Set.S)
You can construct new signatures that specify the exact function show you need:
module MySet_Make(S : sig
include Set.S
val show : elt -> string
end) = struct
let my_method _set el =
print_endline (S.show el);
Some [el]
end
Then you can build the actual module instance by constructing the module with the needed function:
module IntSet = struct
include Set.Make(Int)
(* For other types, this function could be created by just using [##deriving show] *)
let show = string_of_int
end
module MyIntSet = MySet_Make(IntSet)
Ok, after a couple of hours more fumbling around in the dark I found a recipe that does everything I wanted...
First we define a "showable" type, representing a module type that has had [##deriving show] (from https://github.com/ocaml-ppx/ppx_deriving#plugin-show) applied to it:
module type Showable = sig
type t
val pp : Format.formatter -> t -> unit
val show : t -> string
end
(I don't know if there's some way to get this directly from ppx_deriving.show without defining it manually?)
Then we re-define and extend the Set and Set.OrderedType (i.e. element) types to require that the elements are "showable":
module type OrderedShowable = sig
include Set.OrderedType
include Showable with type t := t
end
module ShowableSet = struct
include Set
module type S = sig
include Set.S
end
module Make (Ord : OrderedShowable) = struct
include Set.Make(Ord)
end
end
I think with the original code in my question I had got confused and used some kind of higher-order functor syntax (?) ...I don't know how it seemed to work at all, but at some point I realised my MySet_Make was returning a functor rather than a module. So we'll fix that now and just use a normal functor.
The other thing we can fix is to make MySet a further extension of ShowableSet ... so MySet_Make will take the element type as a parameter instead of another Set type. This makes the eventual code all simpler too:
module type MySet = sig
include ShowableSet.S
val my_method : t -> elt -> elt list option
val show_el : elt -> string
end
module AdjacencySet_Make (El : OrderedShowable) : AdjacencySet
with type elt = El.t
= struct
include ShowableSet.Make(El)
let my_method set el = Some [el] (* whatever *)
let show_el el = El.show el (* we can use the "showable" elements! *)
end
Then we just need an OrderedShowable version of Int as the element type. Int is already ordered so we just have to extend it by deriving "show" and then we can make a concrete MySet:
module Int' = struct
include Int
type t = int [##deriving show]
end
module MyIntSet = MySet_Make(Int')
And we can use it like:
# let myset = MyIntSet.of_list [3; 2; 8];;
# print_endline (MyIntSet.show_el 3);;
"3"

OCaml Typechecking Problem When Using Two Parameterized Modules

I have two modules, Graph and Game, which are parametrized by other modules. They also contain functions f and g which cause typechecking problems when I use them in a testing module. I left out lots of the code that is not important for this problem.
Here is a Graph module that has some module AbstractUSet. AbstractUSet.t is used a lot in the original code. The function f shall later take another function and do some work. The problem is, that the other function comes from another module and has a different type.
module UTYPE = sig
type t
val compare : t -> t -> int
end
module type GRAPH = sig
module U : UTYPE
module AbstractUSet : Set.S
val f : (AbstractUSet.t -> AbstractUSet.t) -> AbstractUSet.t -> AbstractUSet.t
end
module Graph (UA : UTYPE) : (GRAPH with module U=UA) = struct
module U=UA
module AbstractUSet = Set.Make(struct type t=U.t let compare=U.compare end)
let f g uset = g uset
end
The other module is the Game module. It does lots of things with AbstractVSet.t. It contains the function g that shall later be input for the function f from the Graph module.
module type GAME_PIECE = sig
type t
val compare : t -> t -> int
end
module type GAME = sig
module P : GAME_PIECE
module AbstractVSet : Set.S
val g : AbstractVSet.t -> AbstractVSet.t
end
module GameInstance (NA : GAME_PIECE) : (GAME with module P=NA) = struct
module P = NA
module AbstractVSet = Set.Make(struct type t=P.t let compare=P.compare end)
let g vset = vset
end
And this is my module for testing. In the very end, both UTYPE and GAME_PIECE are the same, but I can't make that clear to OCaml. I've commented the lines that don't typecheck. The compiler says there are clashes between MyGame.AbstractVSet.t and MyGraph.AbstractUSet.t.
module TestGame = struct
include(Game(struct type t=string let compare=compare end))
end
module TestGraph = struct
include(Graph(struct type t=string let compare=compare end))
end
module Test = struct
module MyGame = TestGame
module MyGraph = TestGraph
let stringlist = ["a";"b"]
let uset = MyGraph.uset_from_ulist stringlist // works fine
let vset = MyGame.vset_from_vlist stringlist // works fine
let result = MyGraph.f (MyGame.g) vset // does not typecheck!
end
If you ask, why I'm using so many modules: The project is a lot bigger than this code extract, and it is intended to be like this ;)
Can anyone help me how I can make it clear to the OCaml compiler that both UTYPE and GAME_PIECE are the same in the Test module??
Thanks a lot for your help!!!
The first issue is that your abstract set modules are different: the result of two functor applications in OCaml are only equals if the functor applications are applied to exactly the same named modules. For instance, in
module A = struct type t = int end
module F(X:sig type t end) = struct type t end
module FA = F(A)
module B = A
module FA' = F(B)
module C = F(struct type t end)
The types FA.t and Fa'.t are the same
let f (x:FA.t): FA'.t = x
But the types C.t and FA.t are different:
let f (x:C.t): FA'.t = x
Error: This expression has type C.t but an expression was expected of type
FA'.t
But this part can be fixed by not using anonymous structure when they are unnecessary:
module Graph (UA : UTYPE) : (GRAPH with module U=UA) = struct
module U=UA
module AbstractUSet = Set.Make(U)
let f g uset = g uset
end
Then, you are left with the "problem" that Game(M).AbstractUSet and
Graph(M).AbstractUSet defines two distinct types. (Note that this probably the right behavior outside of tests). For the test, one option is to simply expose the information that those modules are the result of functor applications. For instance, one can redefine the GAME module type (and GameInstance functor) to:
module type GAME = sig
type set
module P : GAME_PIECE
module AbstractVSet: Set.S with type t = set
val g : AbstractVSet.t -> AbstractVSet.t
end
module GameInstance (NA : GAME_PIECE) :
(GAME with type set:= Set.Make(NA).t and module P=NA) = struct
module P = NA
module AbstractVSet = Set.Make(NA)
let g vset = vset
end
Here, we have ketp the information that GameInstance(M).AbstractVSet.t is the
same type as Set.Make(M).t.
Combined wit the same operation on the graph part:
module type GRAPH = sig
type set
module U : UTYPE
module AbstractUSet : Set.S with type t = set
val f : (AbstractUSet.t -> AbstractUSet.t) -> AbstractUSet.t -> AbstractUSet.t
end
module Graph (UA : UTYPE) :
(GRAPH with type set := Set.Make(UA).t and module U=UA) = struct
module U=UA
module AbstractUSet = Set.Make(UA)
let f g uset = g uset
end
we are preserving enough type information to keep the equality for the test:
module TestGame = GameInstance(String)
module TestGraph = Graph(String)
module Test = struct
module MyGame = TestGame
module MyGraph = TestGraph
let result vset = MyGraph.f (MyGame.g) vset (* does typecheck *)
end

Use abstract module as part of type definition separate from module

I'm trying to use the module type Partial_information which is constructed via the functor Make_partial_information as the type of the field contents in the type Cell.t. However, I'm getting the error Unbound module Partial_information.
open Core
(* module which is used as argument to functor *)
module type Partial_type = sig
type t
val merge : old:t -> new_:t -> t
end
(* type of result from functor *)
module type Partial_information = sig
type a
type t = a option
val merge : old:t -> new_:t -> t
val is_nothing : t -> bool
end
(* The functor *)
module Make_partial_information(Wrapping : Partial_type):
(Partial_information with type a = Wrapping.t)
= struct
type a = Wrapping.t
type t = a option
let merge ~(old : t) ~(new_ : t) =
match (old, new_) with
| (None, None) -> None
| (None, Some a) -> Some a
| (Some a, None) -> Some a
| (Some a, Some b) -> (Wrapping.merge ~old:a ~new_:b) |> Some
let is_nothing (it: t) : bool = (is_none it)
end
(* Checking to make sure understanding of functor is correct *)
module Int_partial_type = struct
type t = int
let merge ~old ~new_ = new_ [##warning "-27"]
end
module Int_partial_information = Make_partial_information(Int_partial_type)
(* Trying to use _any_ type which might have been created by the functor as a field in the record *)
module Cell = struct
type id = { name : string ; modifier : int }
type t = {
(* Error is here stating `Unbound module Partial_information` *)
contents : Partial_information.t ;
id : id
}
end
Module types are specifications for modules. They do not define types by themselves. They are also not constructed by functors in any way.
Consequently, it is hard to tell what you are trying to do.
As far I can see, you can simply define your cell type with a functor:
module Cell(P : Partial_information) = struct
type id = { name : string ; modifier : int }
type partial
type t = {
contents : P.t;
id : id
}
end
Or it might be even simpler to make the cell type polymorphic:
type 'a cell = {
contents : 'a;
id : id
}
since the type in itself is not particularly interesting nor really dependent upon
the type of contents.
P.S:
It is possible to use first class modules and GADTs to existentially quantify over a specific implementation of a module type. But it is unclear if it is worthwhile to explode your complexity budget here:
type 'a partial_information = (module Partial_information with type a = 'a)
module Cell = struct
type id = { name : string ; modifier : int }
type t = E: {
contents : 'a ;
partial_information_implementation: 'a partial_information;
id : id
} -> t
end

How to use a functor in OCaml

I can't find on the internet how to use the functor I've written. I will post a minimal code, if you need more contextual information tell me and I'll add, but I'm sure it's really easy to do.
I think I just don't understand what a functor is, I see things like this (I will use an analogy with Java to ilustrate my understanding since I'm new to OCaml) :
sig (=) Interface MyInterface
struct (=) Object implements MyInterface
functor (=) MyInterfaceBis extends MyInterface
The following example I'm about to give is stupid, it's just so I can understand the concept behind it :
module type tF = sig
type 'a t
val create : 'a t
end
module F : tF = struct
type 'a t = 'a list
let create = []
end
module type tF2 = functor(F : tF) -> sig
val foo : 'a F.t -> 'a F.t
end
module F2 : tF2 = functor(F : tF) -> struct
let foo f = f
end
I know I can do for example :
let test = F.create
But I don't know how to use F2.
I've tried this page but it's not using my notation and I was more confused after than before.
F2 takes in a module with type tF and produces a module with one function foo:
module NewF = F2 (F)
For more information, see the section about functors in Real World OCaml.

Can we pass functor as an argument to another functor?

I want to know if we can have a local module inside the module. This can be achieved if a functor can be passed as an argument to another functor. But I am not sure if we can do that.
My apologies if this is a vague question.
Thanks.
Yes, it is possible to define higher-order functors. Here is a simple example of a functor that applies its first argument to its second argument:
module App (F : functor (X: sig end) -> sig end) (X: sig end) = F (X)
This is however unrelated to the question of having local modules, which are very straightforward and do not require functors. The following example defines a submodule B that remains private to A:
module A : (sig val g : unit -> unit end) = struct
module B = struct
let f () = print_endline "Hello"
end
let g = B.f
end
let () = A.g () (* valid, prints Hello *)
let () = A.B.f () (* invalid *)