3

I am trying to define a Point module that defines a type to represent 2d points.

I would also like to include a submodule Point.Set so that Point.Set.t is a type meaning 'a set of Points'. That seems logical and convenient, but I am not able to figure out how to make the 'circular' reference that this involves.

I tried this:

file: point.ml (implicitly defines a 'Point' module)

type t = {x: int; y:int}

let compare {x=x1;y=y1} {x=x2;y=y2} = ...implementation omitted for brevity...

module Set = Stdlib.Set.Make(Point)
                         (*  ^^^^^ Internal path Mylib__Point is dangling *)

When I dune build the Mylib project/library this is in. I get an error:

Internal path Mylib__Point is dangling.
  The compiled interface for module Mylib__Point was not found.

I am not entirely sure what the error really means, but I gather it probably has something to do with the fact that we are trying to reference the Point module from within itself. And maybe that is not allowed?

I can work around this by instead defining a separate 'pointSet.ml' file and in there have include Set.Make(Point). Now I have a module called PointSet. That is okay, but I still would find it a bit more 'aesthetically pleasing' if Point.Set could be a submodule of Point instead. Is there a way to make this work?

Ken White
  • 123,280
  • 14
  • 225
  • 444
Kris
  • 3,898
  • 1
  • 23
  • 32
  • Does this answer your question? [Dealing with circular dependencies in OCaml](https://stackoverflow.com/questions/36260/dealing-with-circular-dependencies-in-ocaml) – Ouroborus May 12 '22 at 03:35
  • No I don't think so, that is about recursive types and recursive functions. This question about references at the module level. I know how to deal with recursive type / recursive functions. But I have no idea how to deal with the circular reference in my example, which I think is of a different nature. – Kris May 12 '22 at 03:38
  • The answer does contain a link to something about recursive modules but the link is broken. That may contain some hints as to what an answer to my question might be. But that answer in itself doesn't really address my question directly. – Kris May 12 '22 at 03:44

2 Answers2

4

If you don't mind a little bit of boilerplate, I think this solution may suit you:

point.ml

module Point = struct
  type t = { x : int; y : int }

  let compare { x = x1; y = _y1 } { x = x2; y = _y2 } = x1 - x2
end

module Set : Set.S with type elt = Point.t = Set.Make (Point)

include Point

You'll have access to Point.Set and since point.ml includes the module Point at the end of the file, you won't have to do Point.Point.compare ... in other files.


[EDIT]

I previously made the modules mutually recursive but in this case it's useless. If you need them to be mutually recursive you'll have to explicit their signatures:

point.ml

module rec Point : sig
  type t

  val compare : t -> t -> int
end = struct
  type t = { x : int; y : int }

  let compare { x = x1; y = _y1 } { x = x2; y = _y2 } = x1 - x2
end

and Set : (Stdlib.Set.S with type elt = Point.t) = Stdlib.Set.Make (Point)

include Point
Lhooq
  • 4,281
  • 1
  • 18
  • 37
  • 2
    The `open` will not affect other modules. Did you mean `include Point`.? There is also no recursive dependency between `Point` and `Set`, thus you can define `Point` (or `Core`) and define `Set` after `Point`. – octachron May 12 '22 at 08:35
  • You're right, I meant include. And for the recursive part, I simplified an example I had where Set and Point were mutually recursive but in this case it's useless. Thanks for the review – Lhooq May 12 '22 at 08:58
2

As far as I know a module doesn't have a name for itself. You can make a module (a struct) just for the purpose of supplying it to the functor Set.Make:

type t = { x : int; y : int }
let compare a b = compare a b

module Set =
    Set.Make(struct type nonrec t = t let compare = compare end)
Jeffrey Scofield
  • 65,646
  • 2
  • 72
  • 108
  • Woah! It looks like that works :-). I guess we are lucky that `Set.Make` only expects a small amount of things (2 instead of 15). It isn't very elegant to have to say 'copy these things from my context' one by one. But at least it works... which is great. – Kris May 12 '22 at 04:08