A proposal by Fabrice Le Fessant: > Here is the proposal for namespaces that I recently wrote. It is > already partially implemented in an SVN branch, but there is still > work to do. OCamlpro already has a partial funding for what remains > to be done (see at the end), so, if you are interested in seeing > this work finished, you might want to consider co-funding it to > speed things up. > > --Fabrice > > Proposal for OCaml Namespaces > ============================= > > A namespace is a path that is added in front of a toplevel module name > (compilation unit) to remove ambiguities between toplevel modules. > > Rationale > --------- > > Namespaces can be used to link different modules with the same > module name from different libraries in the same executable. > > Namespaces and modules > ---------------------- > > Namespaces use the same notations as modules: a namespace is > composed of capitalized identifiers ([A-Z][a-zA-z0-9_]*) separated by > dots. This notation allows to switch easily between namespaces and > modules. > > A module can be qualified by a namespace by prefixing the module > name by the namespace name and a dot. QM = NS.M > > Adding a module to a namespace > ------------------------------ > > The namespace of a module is specified with the "-ns NS" argument to > the compiler. > > It could also be specified by a special notation at the beginning of > the file. For example: > > "in namespace NS" > > or better: > > "in module NS.M" > > (I would personally vote against, as it would prevent including the > source of the module in another module, such as "ocp-pack" does for > sources. But if we go for it, I would prefer the second one, that > specifies both the namespace name and the toplevel module name. The > compiler would then check that the current source is either "M.ml" or > "M.mli". However, it is yet unclear, but a construct might simplify a > lot the work of ocamldep with namespaces.) > > The module can then be referenced from other modules using the fully > qualified name "NS.M". It cannot be referenced using only "M", even if > its interface file can be found directly in the load path (except in > "compatibility mode"). > > Compatibility mode > ------------------ > > When neither the option -ns, nor the construction "in > namespace/module" are specified, the compiler does not care about > namespaces (it is in "compatibility mode"). When a module name is > specified, it chooses the first matching toplevel module name, without > taking the namespace specification (if one is available in the object > file) into account. However, it will save the dependency towards the > fully qualified module in the object file (i.e. including the > namespace name), so that linking will still take namespaces into > account. This mode should allow libraries developed before namespaces > to compile with no problem with the new version. > > Namespaces and modules > ---------------------- > > Namespaces cannot be used instead of modules. The only construct > where they can be used instead of modules is "open NS" (and of course > "let open NS in"). They cannot be used instead of M in the following > constructs "include M", "Make(M)", "module N = M". > > Module Aliasing > --------------- > > Since namespaces cannot be used in "module N = M" to change their > name locally, we propose to introduce a new construct "open NS as NS2" > (and "let open NS as NS2 in ...") to alias them locally. The same > construct would be useful for modules too, to alias them without > importing them (and thus forcing a link even when just a type would be > useful), as the "module N = M" would. > > Name priority > ------------- > > If both a module and a namespace share the same name, the module > always hides the namespace. If two modules have the same qualified > name, the first in the load path hides the other ones. > > Values in namespaces > -------------------- > > It is sometimes useful to have values, types and exceptions directly > available in a namespace. For that, any value in any module in any > namespace "NS.Pervasives" is automatically available when namespace > "NS" is referenced. If two values have the same name, the first one in > the path hides the other ones; if they are available in the same > directory in the load path, the value in the first module in > alphabetical order hides the other ones. > > Namespaces and directories > -------------------------- > > Until now, there is no way for the compiler to guess the namespace > of an object file without opening the file. This means that, when > compiling a module using namespaces, the compiler would need to open > all existing object files to discover which namespaces they are > included in. For example, if a module uses "open X", and no "x.cmi" is > available in the path, the only way to know if X is a namespace is by > opening all interface files to check if one of them is in namespace > X. This would be very expensive to do and would probably compromise > the automatic computation of dependencies too. > > Thus, we propose different mechanisms to tell the compiler which > namespaces are available without loading all the interface object > files: > > - Any sub-directory in a path might be the name of a namespace, and > object files in that sub-directory are modules in the namespace (and > sub-directories are sub-namespaces); Note that the same directory > might appear in different directories of the load path, so that all > the included modules will appear to belong to the same namespace; > > - In any directory, all files must belong to the same namespace. > A special file "ocaml.ns" can be used to specify that namespace > (text file, containing the namespace on the first line). > > "ocaml.ns" can also be used when compiling a file, to discover which > namespace it is in, if no "-ns NS", or "in namespace NS" is provided (if > they are provided, the compiler should check they share the same > value). > > Compatibility of build systems > ------------------------------ > > "ocamldep" should be easy to adapt to namespaces when printing makefiles > dependencies. Build systems using the -modules option of ocamldep will > need to be modified to handle namespaces correctly. It is the case of > "ocamlbuild", "omake" and "ocp-build" (our own build tool). The main > challenge is probably "omake", as it is not supported anymore by anybody > so we would need to decide if it is worth adapting it or not. > > Current Implementation > ---------------------- > > There is already a partial implementation of this proposal in the SVN. > What is NOT yet done: > - the "in namespace NS" construct (only the -ns NS option is implemented) > - values in namespaces (Pervasives sub-namespace) > - support for "ocaml.ns" files > - changes to "ocamldep" and "ocamlbuild" > - testing, a lot of testing ! > Here is a proposal to extend my former proposal to fix ocamldep > problems, using ideas from Scala: > > Every source file using namespaces should begin with: > > in namespace NS0 > with import > NS1 > and NS2._ > and NS3.{ _ } > and NS4.{A;B;C} > and NS5.{A->B; B->_; _} > and NS6.{X;Y;Z} as NS7 > ;; > > The new construct tells that: > - the current module is in NS0 > - it uses some modules of NS1 (not specified which ones) > - it uses some modules of NS2 (not specified which ones) > - it uses some modules of NS3 (not specified which ones) > - it uses modules A, B and C of namespace NS4 > - of NS5, it uses module A, but locally renamed as B, it does not use > the original B module, and uses all the other modules > - it uses modules X, Y and Z of NS6, and locally defines a namespace > NS7 composed of the values included in these 3 modules (this replaces > the Pervasives sub-namespace mechanism, and allows to specified > exactly the order of modules if necessary). > > "ocamldep" can use this construct to exactly know which namespaces are > available in the module, and which modules of these namespaces are > used. If the modules are not specified, as for NS1, NS2 and NS3 in the > preceeding example, it might infer false dependencies, but such > dependencies can be removed by manually removing them from the > namespaces (using the NS.{ X -> _ } construct to remove module X from > NS). > > Once this construct as been put at the beginning of the source, > namespaces are used just as modules (you still need to use "open NS" > even if you used "import NS", just as in Java/Scala). > > I think this would completely fix the problem of dependencies, and > provide powerful hierarchical and extensible namespaces.