Allow local form of arbitrary structure items#13835
Allow local form of arbitrary structure items#13835nojb wants to merge 15 commits intoocaml:trunkfrom
Conversation
|
The first part, where you reimplement existing constructions using a new generic construction, sounds like unilaterally good to have, it is easier to review, and it does not require design-consensus-building because it does not change the language. Have you considered sending it as a prefix PR to get the ball rolling? |
Good idea. I will split that part into its own PR. Thanks! |
Done in #13839 |
|
I made a note to discuss the language change during the next caml-devel meeting. |
|
This sounds useful! I have wished for What is the intended semantics of |
Yes, I didn't mention it explicitly in the description, but the semantics I had in mind was that the scope of the attributes would extend over
Accordingly, I would expect the warning not to be raised here. |
Currently OCaml supports local forms (= scoped to an expression) of a few specific structure items:
let exception,let openandlet module.However, other structure items that could be useful are not available "locally":
let type t =,let type t +=,let class,let module type, etc.The perspicacious reader will tell me that in fact, this is not quite true and that one can use
let open structto extend the range of items that can be bound locally. For example:let open struct type t = T end in ewill declare the typet = Tlocally ine.However, the current state of things has a number of downsides:
let module,let open,let exceptionis duplicated in various ways, using their own dedicated AST nodes, between the code that deals with structure items in their "global" and "local" forms.let open structconstruct involves the allocation of an extra block (corresponding to thestruct) and is a bit heavy syntactically.let exceptionexists, butlet type t +=does not, given that they are essentially the same concept.The one justification I can think for the last point of is that adding a new local form for an existing structure item is a heavy undertaking (need to modify the compiler in a few dozen places). Instead, this PR proposes to uniformize and generalize the treatment of local forms of structure items, so that this handling is done once (in this PR), and in the future, any new structure item will automatically be supported "locally", without needing any extra implementation effort. More precisely:
let structure-item in expr. This generalizes the existinglet open,let exceptionandlet module. (First four commits, up to and includingAdd Pexp_stritem, Texp_stritem.) Note that this only involves the middle- and back-end (typechecking and code generation).let open,let exceptionandlet modulein terms of this one construct. These commits only touch the parser, but indirectly they are switching the typechecking and code generation logic for these three constructs to a shared one for all structure items. (Three commits:Reimplement ....)At this point, we have neither gained nor lost anything as far as the surface language is concerned. However, we have gained in the implementation level: the old AST nodes
Pexp_letmodule,Pexp_openandPexp_letexception(and their typed avatars) have become dead code, replaced by a single node (Pexp_stritem/Texp_stritem). More importantly, the typechecking and code generation for this one construction is fully shared with that used for ordinary structure items. The next step is to expose this abstract construct in the surface language.LET structure_item IN seq_expr, and the ad-hoc ones forlet exception,let openandlet moduleare removed. Actually, something a bit more complex is done to disallow certain forms (see below), as well as to account for the different way attributes are handled in local forms of structure items1.After this step (commits
parser: propagate ...andAllow local forms ...), the surface language is endowded with the following local forms of structure items:The first three were already supported:
let open ... in exprlet exception ... in exprlet module ... in exprThe following ones are newly supported:
let type t = ... in exprlet type t += ... in exprlet class c = ... in exprlet class type c = ... in exprlet module type T = ... in exprlet external f : .... in exprlet [@@@foo] in exprThe following ones are disallowed:
let let p = ... in exprlet include ... in exprThe first one is disallowed for usability reasons and the second one because I could not think of any use for it (one would use
let openinstead). Finally,let module,let openandlet exception. (Last commit:Remove reimplemented ...)That's it. To summarize: the PR proposes to treat the derivation from structure item to "local structure item" in a systematic way, both at the parser level, AST level, typechecking and code generation, so that any new structure item will be automatically available in a local form. Along the way, we expose in the surface language local forms of existing structure items which were not available today (at least without passing via the
let open structconstruct).All opinions warmly welcome! I don't think the review of the PR is very difficult (each individual commit is rather small and self-contained), but we will need to agree first on whether we want to move in this direction (or not).
cc @yallop, whose comment in https://discuss.ocaml.org/t/seq-of-iter-a-contained-use-case-for-effects/16187/5?u=nojb spurred me to look at this again.
What is missing?
What needs special attention
typecore.mldealing with the typechecking for the new constructlet structure-item in expr. There is some logic to make sure that the type ofexprmakes sense outside of the scope of thestructure-item. However, I do not have a good understanding of it, so an expert will need to take a look and explain it to me :)translcore.mldealing with the code-generation for the new construct. I am fairly confident about this part, but I would appreciate an expert pair of eyes here as well.Potential downsides
This PR will involve a fair amount of churn on the
ppxlibside and/orcompiler-libsusers, but it should be fairly manageable, I think (especially because these local forms are infrequently used).Footnotes
In
let exception[@foo] E in expr, the attributefoois attached to the wholelet exception E in exprand not only theexception Estructure item. ↩