Paths ===== There are various type declarations for values that allow us to specify items within an OCaml signature. These are Identifiers, Paths, Fragments and References. Preamble for the following examples: ```ocaml env=e1 (* Prelude *) #require "odoc.xref_test";; open Odoc_xref2;; open Odoc_xref_test;; #install_printer Common.root_pp;; #install_printer Odoc_model.Names.ValueName.fmt;; #install_printer Odoc_model.Names.ModuleName.fmt;; #install_printer Odoc_model.Names.ModuleTypeName.fmt;; #install_printer Odoc_model.Names.TypeName.fmt;; #install_printer Odoc_model.Names.PageName.fmt;; let id = Common.id;; let mod_sig = let open Common.LangUtils.Lens in Module.type_ |-~ Module.decl_moduletype |-~ ModuleType.expr_signature let functor_sig = let open Common.LangUtils.Lens in Module.type_ |-~ Module.decl_moduletype |-~ ModuleType.expr_functor |-- snd |-~ ModuleType.expr_signature let functor_arg_sig = let open Common.LangUtils.Lens in Module.type_ |-~ Module.decl_moduletype |-~ ModuleType.expr_functor |-- fst |-~ FunctorParameter.named |-- FunctorParameter.expr |-~ ModuleType.expr_signature ``` Identifiers ----------- Identifiers are the most basic way to describe a specific element in a signature. The type is split into specific subtypes for specific types of elements, and there is a union type to which all identifiers can be cast. For example, we have the type `Identifier.type_` for a type declaration. This is: ```ocaml skip type type_ = [ | `Type of signature * TypeName.t | `CoreType of TypeName.t ] ``` so we either have named type declarations that are associated with a particular signature, or core types that aren't (e.g. `int`). The `signature` type is anything that can contain elements such as these type declarations: ```ocaml skip type signature = [ | `Root of Root.t * UnitName.t | `Module of signature * ModuleName.t | `Parameter of signature * ModuleName.t | `Result of signature | `ModuleType of signature * ModuleTypeName.t ] ``` A `Root` refers to a compilation unit (think 'mli' file) - these can clearly contain type declarations. The `` `Module `` and `` `ModuleType `` constructors are fairly obvious - these also have a parent `signature` and are named. The `` `Parameter `` constructor refers to the parameter of a functor, and the `` `Result `` constructor is used to refer to the definitions of the result of the functor. As an example: ```ocaml env=e1 let test_data = {| module type S = sig type t end module M : sig type m_t = int end module F(X : sig type foo end) : sig open X type f_t = foo end |};; let sg = Common.signature_of_mli_string test_data;; ``` Here we have: - S: `` `ModuleType (`Root root, "S") ``; ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_type "S" |-- ModuleType.id) sg);; - : Odoc_model.Paths.Identifier.ModuleType.t = {Odoc_model__Paths_types.iv = `ModuleType ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, S); ihash = 527535255; ikey = "mt_S.r_Root.p_None"} ``` - M: `` `Module (`Root root, "M") `` ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_ "M" |-- Module.id) sg);; - : Odoc_model.Paths.Identifier.Module.t = {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"} ``` - F: `` `Module (`Root root, "F") `` ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_ "F" |-- Module.id) sg);; - : Odoc_model.Paths.Identifier.Module.t = {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, F); ihash = 748202139; ikey = "m_F.r_Root.p_None"} ``` - m_t: `` `Type ( `Module ( `Root root, "M"), "m_t" ) `` ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_ "M" |-- mod_sig |-- Signature.type_ "m_t" |-- TypeDecl.id) sg);; - : Odoc_model.Paths.Identifier.Type.t = {Odoc_model__Paths_types.iv = `Type ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}, m_t); ihash = 493774927; ikey = "t_m_t.m_M.r_Root.p_None"} ``` - f_t: `` `Type (`Result (`Module (`Root root, "F")), "f_t") `` ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_ "F" |-- functor_sig |-- Signature.type_ "f_t" |-- TypeDecl.id) sg);; - : Odoc_model.Paths.Identifier.Type.t = {Odoc_model__Paths_types.iv = `Type ({Odoc_model__Paths_types.iv = `Result {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, F); ihash = 748202139; ikey = "m_F.r_Root.p_None"}; ihash = 709672416; ikey = "___result__.m_F.r_Root.p_None"}, f_t); ihash = 344808614; ikey = "t_f_t.___result__.m_F.r_Root.p_None"} ``` - foo: `` `Type (`Parameter (`Module (`Root root, "F"), "X"), "foo") `` ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_ "F" |-- functor_arg_sig |-- Signature.type_ "foo" |-- TypeDecl.id) sg);; - : Odoc_model.Paths.Identifier.Type.t = {Odoc_model__Paths_types.iv = `Type ({Odoc_model__Paths_types.iv = `Parameter ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, F); ihash = 748202139; ikey = "m_F.r_Root.p_None"}, X); ihash = 930266402; ikey = "p_X.m_F.r_Root.p_None"}, foo); ihash = 212207131; ikey = "t_foo.p_X.m_F.r_Root.p_None"} ``` There are many other types of Identifier: type, constructor, field, etc. Paths ===== A Path is a 'user visible' description of an identifier. Whenever a 'dot' is typed this is usually a path. Initially the compiler will specify exactly only the first element of the path, and the subsequent components of the path will just be strings. For example, ```ocaml env=e1 let example = {| module M : sig module N : sig type t type x1 = t end type x2 = N.t end type x3 = M.N.t |} let sg = Common.signature_of_mli_string example;; ``` All three types `x1`, `x2` and `x3` are pointing at `M.N.t`. Here we define a lens to extract the manifest of a type: ```ocaml env=e1 let type_constr_path name = let open Common.LangUtils.Lens in Signature.type_ name |-- TypeDecl.equation |-- TypeDecl.Equation.manifest |-~ option |-~ TypeExpr.constr |-- fst ``` and now we can get the paths for all three type declarations: ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_ "M" |-- mod_sig |-- Signature.module_ "N" |-- mod_sig |-- type_constr_path "x1") sg);; - : Odoc_model.Paths.Path.Type.t = `Identifier ({Odoc_model__Paths_types.iv = `Type ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}, N); ihash = 1041581453; ikey = "m_N.m_M.r_Root.p_None"}, t); ihash = 311238448; ikey = "t_t.m_N.m_M.r_Root.p_None"}, false) # Common.LangUtils.Lens.(get (Signature.module_ "M" |-- mod_sig |-- type_constr_path "x2") sg);; - : Odoc_model.Paths.Path.Type.t = `DotT (`Identifier ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}, N); ihash = 1041581453; ikey = "m_N.m_M.r_Root.p_None"}, false), t) # Common.LangUtils.Lens.(get (type_constr_path "x3") sg);; - : Odoc_model.Paths.Path.Type.t = `DotT (`Dot (`Identifier ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}, false), N), t) ``` We can resolve the paths: ```ocaml env=e1 let sg' = Common.compile_signature sg;; ``` and now the paths are: ```ocaml env=e1 # Common.LangUtils.Lens.(get (Signature.module_ "M" |-- mod_sig |-- Signature.module_ "N" |-- mod_sig |-- type_constr_path "x1") sg');; - : Odoc_model.Paths.Path.Type.t = `Resolved (`Identifier {Odoc_model__Paths_types.iv = `Type ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}, N); ihash = 1041581453; ikey = "m_N.m_M.r_Root.p_None"}, t); ihash = 311238448; ikey = "t_t.m_N.m_M.r_Root.p_None"}) # Common.LangUtils.Lens.(get (Signature.module_ "M" |-- mod_sig |-- type_constr_path "x2") sg');; - : Odoc_model.Paths.Path.Type.t = `Resolved (`Type (`Identifier {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}, N); ihash = 1041581453; ikey = "m_N.m_M.r_Root.p_None"}, t)) # Common.LangUtils.Lens.(get (type_constr_path "x3") sg');; - : Odoc_model.Paths.Path.Type.t = `Resolved (`Type (`Module (`Identifier {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}, N), t)) ``` So the difference in the paths is essentially at what point we switch to the identifier. We also encode other information into the path during the resolution process. This takes the form of additional constructors embedded into the path. These are: ### Subst The `` `Subst `` construct is declared as: ```ocaml skip | `Subst of module_type * module_ ``` and is used in a quite specific place - when a functor argument contains a module type declaration, and the functor body contains a module that is contrained by that signature. For example: ```ocaml env=e1 let example = {| module type ARG = sig module type S end module F : functor (X : ARG) -> sig module N : X.S end module M : sig module type S = sig type t end end type t = F(M).N.t |};; ``` ```ocaml env=e1 # let sg = Common.compile_signature (Common.signature_of_mli_string example) ;; val sg : Odoc_model.Lang.Signature.t = {Odoc_model.Lang.Signature.items = [Odoc_model.Lang.Signature.ModuleType {Odoc_model.Lang.ModuleType.id = {Odoc_model__Paths_types.iv = `ModuleType ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, ARG); ihash = 379411454; ikey = "mt_ARG.r_Root.p_None"}; source_loc = None; doc = {Odoc_model__.Comment.elements = []; warnings_tag = None}; canonical = None; expr = Some (Odoc_model.Lang.ModuleType.Signature {Odoc_model.Lang.Signature.items = [Odoc_model.Lang.Signature.ModuleType {Odoc_model.Lang.ModuleType.id = {Odoc_model__Paths_types.iv = `ModuleType ({Odoc_model__Paths_types.iv = `ModuleType ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, ARG); ihash = 379411454; ikey = "mt_ARG.r_Root.p_None"}, S); ihash = 208722936; ikey = "mt_S.mt_ARG.r_Root.p_None"}; source_loc = None; doc = {Odoc_model__.Comment.elements = []; warnings_tag = None}; canonical = None; expr = None}]; compiled = true; removed = []; doc = {Odoc_model__.Comment.elements = []; warnings_tag = None}})}; Odoc_model.Lang.Signature.Module (Odoc_model.Lang.Signature.Ordinary, {Odoc_model.Lang.Module.id = {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, F); ihash = 748202139; ikey = "m_F.r_Root.p_None"}; source_loc = None; doc = {Odoc_model__.Comment.elements = []; warnings_tag = None}; type_ = Odoc_model.Lang.Module.ModuleType (Odoc_model.Lang.ModuleType.Functor (Odoc_model.Lang.FunctorParameter.Named {Odoc_model.Lang.FunctorParameter.id = {Odoc_model__Paths_types.iv = `Parameter ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, F); ihash = 748202139; ikey = "m_F.r_Root.p_None"}, X); ihash = 930266402; ikey = "p_X.m_F.r_Root.p_None"}; expr = Odoc_model.Lang.ModuleType.Path {Odoc_model.Lang.ModuleType.p_expansion = Some (Odoc_model.Lang.ModuleType.Signature {Odoc_model.Lang.Signature.items = [Odoc_model.Lang.Signature.ModuleType {Odoc_model.Lang.ModuleType.id = {Odoc_model__Paths_types.iv = `ModuleType ({Odoc_model__Paths_types.iv = `Parameter ({Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, F); ihash = 748202139; ikey = "m_F.r_Root.p_None"}, X); ihash = 930266402; ikey = "p_X.m_F.r_Root.p_None"}, S); ihash = 313393860; ikey = "mt_S.p_X.m_F.r_Root.p_None"}; source_loc = None; doc = {Odoc_model__.Comment.elements = []; warnings_tag = None}; canonical = None; expr = None}]; compiled = true; removed = []; doc = {Odoc_model__.Comment.elements = []; warnings_tag = None}}); p_path = `Resolved (`Identifier {Odoc_model__Paths_types.iv = `ModuleType ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, ARG); ihash = 379411454; ikey = "mt_ARG.r_Root.p_None"})}}, Odoc_model.Lang.ModuleType.Signature {Odoc_model.Lang.Signature.items = [Odoc_model.Lang.Signature.Module (Odoc_model.Lang.Signature.Ordinary, {Odoc_model.Lang.Module.id = {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Result {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p"... (* string length 13; truncated *)}, F); ihash = 748202139; ikey = "m_F.r_Ro"... (* string length 17; truncated *)}; ihash = 709672416; ikey = "___resul"... (* string length 29; truncated *)}, N); ihash = ...; ikey = ...}; source_loc = ...; doc = ...; type_ = ...; canonical = ...; hidden = ...}); ...]; compiled = ...; removed = ...; doc = ...})); canonical = ...; hidden = ...}); ...]; compiled = ...; removed = ...; doc = ...} ``` The problem here is that odoc will not generate a page for the module `F(M)`. Normally links into the body of the functor would simply link to the declaration of the functor itself, but in this case there is no `type N.t` there. What we have to do instead is link to the signature that contains the type - in this case `M.S`. Since we necessarily need to have located the definition of `t` during the resolution process we embed it into the returned resolved path as this `` `Subst `` constructor: ```ocaml env=e1 # Common.LangUtils.Lens.(get (type_constr_path "t") sg) ;; - : Odoc_model.Paths.Path.Type.t = `Resolved (`Type (`Subst (`ModuleType (`Substituted (`Identifier {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}), S), `Module (`Apply (`Identifier {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, F); ihash = 748202139; ikey = "m_F.r_Root.p_None"}, `Identifier {Odoc_model__Paths_types.iv = `Module ({Odoc_model__Paths_types.iv = `Root (Some {Odoc_model__Paths_types.iv = `Page (None, None); ihash = 236059787; ikey = "p_None"}, Root); ihash = 818126955; ikey = "r_Root.p_None"}, M); ihash = 716453475; ikey = "m_M.r_Root.p_None"}), N)), t)) ``` This way we can render the path as `F(M).N.t` but actually link to `M.S.t` in the html. ### SubstAlias