Skip to content

OxCaml: support for parameterized libraries - #1464

Open
art-w wants to merge 2 commits into
ocaml:masterfrom
art-w:oxcaml-param
Open

OxCaml: support for parameterized libraries#1464
art-w wants to merge 2 commits into
ocaml:masterfrom
art-w:oxcaml-param

Conversation

@art-w

@art-w art-w commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

This PR integrates OxCaml parameterized libs in Odoc, by adding the missing informations (is that library a description of a parameter? is that library parameterized and by what? does that library implement a parameter?) and fixing the broken references to instantiated parameterized libraries. The later is similar to a functor application, with the difference that arguments are nominal instead of positional: The OxCaml compiler internally uses the syntax Lib[Param:Impl], which we replicate (with individual links on Lib, Param and Impl).

See the cram test or dune's documentation on parameterized libs

Fixes #1390

Alizter added a commit to ocaml/dune that referenced this pull request Aug 25, 2026
The odoc generation was skipping all libraries with an `implements`
field (= assuming virtual modules), which meant that the documentation
of OxCaml parameterized libraries was missing important pages (see
ocaml/odoc#1464)

Everything builds and odoc generates documentation for all the libraries:

$ dune build @doc-private 2>&1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using @doc-private because otherwise dune skips the documentation of libraries with an implements (fixed in the next release of dune by ocaml/dune#16200 )

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good hint, maybe worth writing that rationale in the test.

Also, come to think of it, it might be worth adding a constraint on dune versions in the OPAM file in the case of with-test (and maybe if we can filter on the oxcaml compiler, so we don't bump the minimal dune version for tests on OCaml)

@art-w

art-w commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

Rebased for OxCaml minus39

@Leonidas-from-XIV Leonidas-from-XIV left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks overall very nice, I only have a few nitpicks.

I am a bit thrown by the spelling of "parameterisation" since I would've used "parametrization". I think it makes sense to adopt the spelling that OxCaml uses, however the docs never seem to mention it so I don't know what their preferred spelling is.


Everything builds and odoc generates documentation for all the libraries:

$ dune build @doc-private 2>&1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good hint, maybe worth writing that rationale in the test.

Also, come to think of it, it might be worth adding a constraint on dune versions in the OPAM file in the case of with-test (and maybe if we can filter on the oxcaml compiler, so we don't bump the minimal dune version for tests on OCaml)


Render to markdown for inspection:

$ for f in $(find _build/default/_doc/_odocls -name '*.odocl' | sort); do

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there's a way to get this without having to inspect the internals of _build? Just to make sure it's not unnecessarily fragile.

The library names below are suffixed with an opaque hash by dune; we normalise
it away so the output is stable.

$ md() { cat markdown/$1@*/$2.md | sed 's/@[0-9a-f]*/@HASH/g'; }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Useless use of cat potentially. But I see the improved readability aspect of it.

> odoc html-generate --indent --sidebar sidebar.odoc-sidebar -o html \
> $(find _build/default/_doc/_odocls -iname "$lib.odocl")
> done
$ grep -rl 'current_unit' html/ | sed 's/@[0-9a-f]*/@HASH/g' | sort

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenBSD grep discourages -r and encourages usage of -R, however that has slightly different semantics in GNU grep. Potentially worth switching to -R, however its not super important.

Comment thread src/xref2/link.ml

and instance_prefix env (m : Paths.Path.Module.t) : Paths.Path.Module.t option =
let unfolded = unfold_alias env m in
if unfolded = m || not (is_instance unfolded) then None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is = safe to use on Path.Path.Module.t values?

Comment thread src/loader/odoc_loader.ml
| None -> raise (Cmt_format.Error (Cmt_format.Not_a_typedtree filename))
#else
let read_cmt_and_parameterisation filename =
(no_parameterisation, Cmt_format.read_cmt filename)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nitpick but the order of items in the name of the function and in the tuple are swapped.

Comment thread src/loader/odoc_loader.ml
in
match cmt with
| Some cmt -> (parameterisation, cmt)
| None -> raise (Cmt_format.Error (Cmt_format.Not_a_typedtree filename))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe have this at the top, since no point of figuring out a parameterisation if cmt is None from the get-go.

Also, why is the error Not_a_typedtree?

Comment thread src/loader/ident_env.ml
List.mem id env.shadowed
module Path = struct

let module_of_id id = `Root (ModuleName.of_ident id)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be more readable to have this in the #else block, to avoid shadowing it in the OXCAML case.

Comment thread src/loader/ident_env.ml
#if defined OXCAML
let rec read_global_name (n : Global_module.Name.t) : Paths.Path.Module.t =
(* OxCaml parameterized library application "Lib[Param:Impl][P2:I2]" *)
let base = `Root (ModuleName.make_std n.head) in

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be Lib in this example, correct?

Comment thread src/document/generator.ml
(Comment.standalone preamble, Comment.standalone first_comment @ items)

let make_expansion_page ~source_anchor url comments items =
let make_expansion_page ?(library_parameter = false) ~source_anchor url comments

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder whether it wouldn't be better to make this a mandatory named param, to avoid accidentally setting this to false by leaving it out. But maybe there's really just one place where it should be set to true.

@jonludlam

Copy link
Copy Markdown
Member

Thanks @art-w ! sorry it's taken me some time to get to this. I think the loader parts look good, and the rendering looks mostly fine (modulo the boolean tacked onto the Page.t).

I'm more hesitant about the resolution parts. I was expecting there to be more changes in tools.ml where we resolve paths, but you're essentially throwing an error there and trying to handle it in link.ml instead. Aside from being in a surprisingly different place, it also means that we don't resolve subpaths, like Lib[Param:Impl].Inner as when we reresolve in link.ml they don't match `ApplyParam. I'd like to see how it looks to handle everything in tools.ml - I've had a very rough attempt here though I'd suggest using that as a suggested direction rather than something to cherry pick.

For the boolean in Page.t, I think it might be better if we get a new kind variant in document/url.ml and then we can just amend make_name_from_path rather than prefixing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add support for representing parameterized libraries

3 participants