Skip to content

Commit d3b9742

Browse files
committed
New RFC: unit headers for OCaml source files
1 parent 2ab45a2 commit d3b9742

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

rfcs/unit-headers.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# Unit headers for OCaml source files
2+
3+
## Context
4+
5+
In OCaml, source files play a double role:
6+
7+
- They are interpreted inside the language as modules, formed by
8+
sequence of structure items. (Modules can be nested, but a file
9+
always acts as a toplevel module.)
10+
11+
- They are interpreted by the compilation tools as "compilation
12+
units", the primary units of compilation and linking, whose
13+
dependencies on other compilation units are tracked and whose
14+
linking order determines the program semantics.
15+
16+
(Technically a compilation unit is formed by a pair of a .ml and
17+
a .mli file, but sometimes only one of them when the other does not
18+
exist.)
19+
20+
21+
Some things that OCaml programmers can express only make sense for
22+
compilation units, not modules. Currently they can only be expressed
23+
through compiler command-line options, typically stored in the build
24+
system. For example:
25+
26+
- Dependencies on other compilation units or (in general)
27+
archives/libraries/packages.
28+
- Global compilation options (-safe-string, -rectypes).
29+
30+
Sometimes it would be convenient, even important, to specify those
31+
aspects in the source code itself, but there is no place in the syntax
32+
to specify them: they are not valid structure items as they don't make
33+
sense inside an arbitrary (nested) module.
34+
35+
### One example of the problem
36+
37+
One example use-case is [@@@warning "-missing-mli"]: we would like to
38+
let users explicitly disable the new `missing-mli` warning
39+
(introduced by [#9407](https://github.com/ocaml/ocaml/pull/9407) in
40+
4.13~dev) inside a particular .ml file, indicating that it
41+
intentionally does not have a corresponding .mli file.
42+
43+
This warning is implemented at the level of compilation units, not
44+
during the checking/compilation of the module code, so the current
45+
implementation of `[@@@warning ..]` does not support disabling it: it
46+
only enables/disable warnings for the following structure items in the
47+
current module.
48+
49+
A proposal exists to change the semantics of toplevel `@@@warning`
50+
attributes to remain in scope for the whole checking/compilation of
51+
the compilation unit, see
52+
[#10319](https://github.com/ocaml/ocaml/pull/10319). This is
53+
a special case of one the two options discussed in this RFC, and it
54+
led to the present discussion.
55+
56+
57+
## Proposals
58+
59+
Two proposals to address this issue, one "implicit" and one
60+
"explicit".
61+
62+
The [@@@warning "-missing-mli"] PR implements the implicit proposal.
63+
64+
I prefer the explicit proposal.
65+
66+
67+
### Implicit proposal: handle toplevel attributes/extensions at the compilation unit level
68+
69+
We could consider that floating attributes and extensions that are at
70+
the toplevel of a file are not interpreted as "normal" structure
71+
items, at the level of the module, but instead as "unit"
72+
attributes/extensions at the level of the unit.
73+
74+
```ocaml
75+
let foo = ...
76+
77+
[@@@warning "-missing-mli"] (* warning setting for the whole compilation unit *)
78+
79+
module Foo = struct
80+
[@@@warning "..."] (* warning setting for a submodule only
81+
end
82+
```
83+
84+
Pros:
85+
86+
1. Reasonably easy to implement, no syntax change (we reinterpret
87+
syntax differently).
88+
89+
2. This is consistent with the way toplevel directives `#foo ;;` are
90+
handled today: toplevel directives are only valid at the toplevel,
91+
but can be mixed with other structure items.
92+
93+
Cons:
94+
95+
1. Confuses two notions.
96+
97+
2 We lose the current property that any OCaml code can be moved inside
98+
a submodule, preserving its meaning.
99+
100+
3. We cannot hope to extend this idea in the future to support global
101+
settings, such as `-rectypes`, because it would be a mess to allow
102+
those to change in the middle of other structure items.
103+
104+
#### Variant
105+
106+
One possible variant of this proposal would be to specify certain
107+
attributes/extensions as "header attributes", that have the same
108+
syntax as floating structure/signature-level attributes/extensions,
109+
but can only be used at the beginning of the file (before any
110+
non-header construct). This solves Cons.3, but aggravates Cons.1 by
111+
creating more surprises for users (certain toplevel floating
112+
attributes can be moved around and other not, etc.).
113+
114+
115+
### Explicit proposal: create a "header" extension for compilation-unit configuration
116+
117+
Instead of implicitly treating toplevel attributes/extensions as
118+
scoping over the whole compilation-unit, we propose a builtin
119+
`ocaml.unit_header` extension whose content should be understood as scoping
120+
over the whole compilation unit, not just a module.
121+
122+
```ocaml
123+
[%%unit_header
124+
[@@@warning "-missing-mli"]
125+
[@@@rectypes]
126+
]
127+
128+
let foo = ...
129+
```
130+
131+
"unit headers" must be before any other structure/signature items
132+
(comments are allowed before headers). They are the only component of
133+
the .ml syntax that cannot be moved into a submodule (doing so results
134+
in an error).
135+
136+
Note: this RFC does *not* propose a new `@@@rectypes` attribute to be
137+
supported here, it is an example of the sort of feature that could,
138+
over time, become available in unit headers. `[@@@warning
139+
"-missing-mli"]` would be immediately adapted to work (only) in unit
140+
headers, but the RFC itself proposes the "header" notion itself, and
141+
not any specific item to be part of it.
142+
143+
144+
#### Future extensions
145+
146+
In the future, certain toplevel directives could be allowed in the
147+
unit header. This is not proposed here.
148+
149+
We could imagine certain tools querying the unit header of source
150+
files for configuration (or querying the compiler to ask for them),
151+
for example to support a "#require ..." directive integrated in the
152+
build system. This is not proposed here, and in fact not necessarily
153+
the best approach.
154+
I think it probably makes more sense to reserve the header for aspects
155+
of OCaml programs that the compiler knows about (that correspond to
156+
command-line options), so that header interpretation is left entirely
157+
in the compiler. If someday we have a compiler that handles
158+
dependencies (and/or ppx resolution, etc.) by itself, then those
159+
aspects would become naturally specifiable in unit headers.

0 commit comments

Comments
 (0)