include functor support for OxCaml - #1452
Conversation
18499bf to
ca9079a
Compare
ca9079a to
a6c9652
Compare
art-w
left a comment
There was a problem hiding this comment.
Thanks a lot, this looks great! I only have minor feedback but nothing blocking :)
| (** This is a Module where the type is named and then included. *) | ||
| module type Make = (_ : sig type t end) -> sig type included end | ||
| type t | ||
| include functor Make |
There was a problem hiding this comment.
Out of curiosity, can we test the behavior with (** @inline *)?
|
I think there's a chunk of logic missing from this PR. At the moment, all it does AFAICT is get the expansion of the functor and splice it into the expansion of the include. While this works for simple examples, it's not sufficient for more involved cases. As a simple example, consider this: module F ( I : sig type t end ) = struct
type myt = I.t
end
module M = struct
type t = float
include functor F
endRunning this through module F : functor (I : sig type t end) -> sig type myt = I.t end
module M : sig type t = float type myt = float endIf we just simply get the signature of the functor body and splice it in, we end up something more like: ...
module M : sig
type t = float
type myt = I.t
endand obviously that I suspect the most straightforward implementation of this would be to do exactly what the docs suggest it does internally. Treat it more like: module M = struct
module __DUMMY__ = struct
type t = float
end
include __DUMMY__
include F(__DUMMY__)
endWe can ensure that the dummy module is hidden, meaning that the include will just inline the contents, |
78e4383 to
e8b5ba1
Compare
Signed-off-by: Marek Kubica <marek@tarides.com>
Signed-off-by: Marek Kubica <marek@tarides.com>
Signed-off-by: Marek Kubica <marek@tarides.com>
e54c667 to
1dbd45c
Compare
1dbd45c to
5997c73
Compare
5997c73 to
ac92802
Compare
|
I've now implemented @jonludlam's proposal of compiling down the include functor to basically a The way it works is by parsing the However there's one outstanding issue: while the items coming from the functor expansions contain the right path (e.g. What I could imagine would be best if I could hide the generated modules but also tell the cross-linker that I want the item to be cross-referenced to the ("include") functor, not the generated module. Is there a way to tell odoc to "redirect" paths? |
|
Odoc has a mechanism to specify a substitution via |
panglesd
left a comment
There was a problem hiding this comment.
I'll continue the review, but here is already some comments. I think the Make functor should be made a little bit more complex, and in particular use its argument.
module Make (T : sig type t end) : sig type included = T.t endNow suppose it is used in the following way:
type t
include functor MakeFrom my understanding of the oxcaml feature, the rendered signature should be:
type t
> include functor Make
type included = t(where > is for the opened include accordion 🪗 )
In my testing, that is not the case!
I wonder if it is possible to fix that using the current "sugar". I believe the "desugared" form of the example above (extended with a value and a module) could use aliases, for types, modules and module types, instead of relying on the DUMMY__ include :
type t
val f : t
module X : S
module DUMMY__ : sig
type nonrec t = t
val f : t
module X = X
end
module STRUCT__Make : Make
include module type of STRUCT__Make(DUMMY__)
endWhat do you think?
Also, definitely we want those generated modules to be hidden. As @jonludlam mentioned, that would turn the "inlining" invisible if we keep it, and odoc would use links to a visible alias.
Also note that while I'd like to investigate the alternate sugar, the current version is better than no support, and as such (I still need more review of course) I would not block the merge on the problem noted above!
|
I had a go at the "alias desugaring" idea: https://github.com/panglesd/odoc/tree/include-functor-alias-desugaring It looks a reasonable first solution, I think. Maybe implementing the "include functor expansion" mechanism in odoc would be better, but slightly more invasive... |
The synthetic module standing in for the functor argument held copies of the
preceding items. Since it is hidden, everything the functor's expansion
inherited from its argument came out as `BODY__n.t` -- a hidden path, which
`Link.type_decl` resolves by looking through it, landing on the copy's own
abstract declaration. So a functor that used its argument, as
`Comparable.Make` does, lost exactly the part worth documenting.
Alias the items instead: a type's manifest points at the real type, a module
becomes an alias of the real module, a module type a path to it. `BODY__n.t`
then reduces to the enclosing signature's own `t`, and is rendered and linked
as such.
Class types are not aliased. A functor can refer to one from its argument, but
odoc does not chase class type aliases the way it chases type manifests, so
such a reference is still left printing the wrapper's hidden name. That is a
pre-existing limitation, reachable without `include functor`:
module Hidden__thing : sig class type ct = object method m : int end end
type from_class = Hidden__thing.ct
Suggested by @panglesd in ocaml#1452.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Rather than put the functor arguments in a module and `include` it, leave them where they were and construct a synthetic module that's just full of aliases to the items. Suggested by @panglesd in ocaml#1452. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This PR is a follow up to #1368 and adds support for
include functorwhere it now displays that the items were included via the functor.