-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Macros by example 2.0 (macro!) #1584
Conversation
Hmm so we are going to have three macro systems? That is, legacy I am curious about the specific, substantive, backwards-incompatible improvements to Macro By Example that motivate this. Because there have been many useful, backwards-compatible improvements proposed that have been shoved to the side in anticipation of procedural macros. |
|
||
mod a { | ||
// Macro privacy (TBA) | ||
pub macro! foo { ... } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the point of the !
after the keyword? I see no reason for that. Perhaps more fitting would be a !
after foo
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consistency with macro_rules!
. Dropping the !
is listed in the alternatives section.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These already are incompatible systems and there’s no reason to have such weird consistency quirk IMO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I don't get the point. There is no compatibility anyways.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See my comment in #1561 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ticki What effect could an import possibly have that the programmer cares about? Macros are only potentially surprisingly at the invocation site.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@solson What if you have a macro named foo!
and a function named foo
, and you want to export them both for other modules to import? Those two names do not conflict; see https://is.gd/QCJ7Iv for an example. How would you disambiguate and allow importing those names independently? Including the !
on the import of the macro seems like it would produce the behavior people would expect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joshtriplett Yeah, it might be necessary for name clashes. @eternaleye also brought up the fact that with procedural macros you would usually want to import the macro, but you might also want to import the fn that implements the macro, which has the same name, so you could call it as a subroutine of another procedural macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@solson: that latter case is especially meaningful because using it as a macro means the compiler needs it at build time, while using it as a function means the crate needs it at run time. As a result, importing both and relying on them being called in different ways does not suffice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I was going to propose extern!
for phase-incrementing imports. See rust-lang/rust#35900 (comment) in the procedural macros 1.1 tracking issue where I brought up the problem.
Well in the short term, four - old and new macros by example, and old and new procedural macros. In the long term, hopefully, both old versions will disappear.
I don't consider this a goal. While it would be nice (it's kind of cute from a language geek perspective), it doesn't bring any material advantages (making macros by example 'pluggable' is about the only real advantage, and I don't think that is a great one, others may differ). So, I'm not trying to avoid this, but if there are benefits we get by not doing it then, we'll take them.
|
How about
How many macro systems do we need to go through before feature requests like that can be considered? I don't mean to sound sarcastic -- I'm excited, but a bit dismayed about continual punting. |
I actually meant "can we implement Macro By Example 2.0 using new procedural macros" as a serious suggestion, not a cute one. If procedural macros are powerful enough and ergonomic enough to do it, then we can iterate on Macro By Example features without arguing an RFC for each one. |
RFC coming up in the next few weeks
part of the new syntax, should be coming soon
export/re-export disappears, replaced by modularisation
backwards compatible, I'm not planning anything here, but I'd be happy to see RFCs building on macros 2.0. In short, I see macros 2.0 as a prerequisite for this kind of stuff - we don't want to expend effort on them until we're sure about the foundations. I don't see macro reform as punting that stuff, more like bringing it closer. |
Awesome!
That's what I wanted to hear :) |
@rfcbot fcp merge This RFC proposes the concept of macros by example 2.0, without defining the details. Together with other RFCs, some parts (e.g., naming and modularisation) could be implemented today. Future RFCs will further specify other aspects of macros 2.0. It is somewhat akin to the proposed concept of motivation RFCs, although there is a small amount of implementation specified. Feedback has been positive, with the only controversial point whether to use An alternative to accepting this RFC now, might be to accept some implementation in the compiler without an accepted RFC and fill out this RFC into a more complete version. |
Team member @nrc has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
Reviewed the RFC again and I like it. I am still pretty strongly in favor of This isn't to say that we should have implementing the APIs to make this possible in our near horizon, just that we shouldn't foreclose on the possibility. Maybe the idea of ever achieving this is unrealistic though. |
I'd like to see |
I don't understand why the thing that defines macro items needs to look like a macro invocation. I'm pretty strongly in favor of plain I find it even more troubling to use the |
Not sure where or even if it was mentioned, but |
So that it might be a macro invokation someday. (I'm very interested in arguments that this wouldn't be possible!) |
I don't know about possible, it just doesn't seem desirable, unless it looked like eddyb's syntax. It really doesn't fit well with pub and being a proper item. |
@withoutboats What language primitive would it expand to? I'd be more interested in figuring out a method of declaration with a pluggable "body". Although purely token-based expansion might not cut it either way. |
From a user's perspective, I can't think of a consistent way of making |
@withoutboats I'm not opposed to a more general macro syntax concept that works for item-like things. I just think the |
The argument that I remember has to do with cross compilation: macros defined in other crates can be used, even if those crates are not compiled for your current target. I'm not sure if this could be easily "simulated" using plugins. |
So, I left my check on this RFC, but to be clear -- all we're basically giving agreement to here is the idea of a macro 2.0 system, right? All details (e.g., syntax, probably even |
@solson When you say it doesn't fit into existing syntax, does that mean you wouldn't favor a macro extension which would allow you to define macros that look like e.g. |
@withoutboats Like I said, I'm not opposed to a more general macro syntax concept that works for item-like things, so I would be fine with that. When I say it doesn't fit into existing syntax, I really mean existing syntax. :) It would fit fine if we introduced this new general thing. (It would fit fine syntactically, at least - @eddyb's question about what it expands into is an important one.) |
@solson but if we don't include the bang now, we can never make it use that new syntax extension? It has to be built into the compiler forever. |
I prefer the name "pattern macro" to "declarative macro". |
@nikomatsakis can you say more? What do you think about the declarative/procedural split? I mostly prefer declarative due to the symmetry, but could be swayed 😄 |
+1 for "pattern macro". That's the only name I've heard for it which might give newcomers a somewhat accurate impression of what the feature actually is. Although I also like the symmetry of "declarative" vs "procedural", the word "declarative" is so much vaguer than "pattern" that it doesn't mean much of anything without context. |
@nikomatsakis That seems potentially confusing with macros that expand to patterns. i.e. one might call |
+1 for "pattern macro" |
Macros by example 2.0. A replacement for `macro_rules!`. This is mostly a placeholder RFC since many of the issues affecting the new macro system are (or will be) addressed in other RFCs. This RFC may be expanded at a later date.
Only major change is moving from `macro!` to `macro` to declare a macro.
"regular macro" - rule-governed, conforming to a pattern. |
The reason I prefer "pattern macro" is that we are applying a series of patterns, one by one, and testing for a match. "Declarative" macro is also ok, but I think that the sense in which "declarative" vs procedural is being used here is ultimately a kind of jargon, and the name just won't "stick" as easily as pattern. I suppose there could be some confusion regarding a macro that expands to a pattern, but I feel like I have basically never categorized a macro by what it expands to in this way -- that is, I don't say "it's an expression macro". I say, "it's a macro that generates an expression". (Actually, I don't say that at all most of the time, since I never seem to find I have to clarify this.) |
Seems fine. I don't care super strongly about this, and I think that reasoning is good too. |
+1 for pattern macro I think pattern matching is an essential part of what you can do with them. And declarative doesn't really tell me anything. It feels a bit academic to call it declarative but is not does not help me understand what it does. |
Please avoid "+1" comments with no content. GitHub added the 👍 reaction so we could avoid getting emails about nothing. :) |
I merged this and forgot to leave a summary at the time. Tracking issue: rust-lang/rust#39412 This RFC is primarily a statement of intent that we will add a new declarative macros system to Rust (macros 2.0). The only real detail is that we will use The other contentious point seems what to call these things. I added some options to the RFC before merging. My preference is for 'declarative macro' and I merged the RFC with that (I don't think it is worth postponing merging just for this). My feeling is that for now we need a name that is useful for implementors since we have to implement the feature, and we don't need to describe it to users (well, not much, anyway) until it is implemented. The main part of the name for me is its opposition to procedural macros, i.e., these are macros defined using declarative syntax, rather than Rust code. I prefer 'declarative' to 'pattern' since there might be declarative macros which do not rely on pattern matching so centrally, and which might be a candidate for macros 2.0 (I don't have anything in mind here, just not shutting any doors). When it comes to describing macros to users, I personally think 'declarative' is fine, but we could also use 'pattern macro' informally - I don't think renaming features should require an RFC or anything. |
Well, You're right. @nrc |
Initial implementation of declarative macros 2.0 Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`. Differences from `macro_rules!` include: - new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }` - declarative macros are items: ```rust // crate A: mod foo { m!(); // use before definition; declaration order is irrelevant pub macro m() {} // `pub`, `pub(super)`, etc. work } fn main() { foo::m!(); // named like other items { use foo::m as n; n!(); } // imported like other items } pub use foo::m; // re-exported like other items // crate B: extern crate A; // no need for `#[macro_use]` A::foo::m!(); A::m!(); ``` - Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc. - Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used. - This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust. - Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate. - `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive). - See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89) for examples. Small example: ```rust mod foo { fn f() { println!("hello world"); } pub macro m() { f(); } } fn main() { foo::m!(); } ``` Limitations: - This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361. - Lints (including stability and deprecation) and `unsafe` are not hygienic. - adding hygiene here will be mostly or entirely backwards compatible - Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates. - pending improvements in how we encode macro definitions in crate metadata - There is no way to "escape" hygiene without using a procedural macro. r? @nrc
Initial implementation of declarative macros 2.0 Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`. Differences from `macro_rules!` include: - new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }` - declarative macros are items: ```rust // crate A: pub mod foo { m!(); // use before definition; declaration order is irrelevant pub macro m() {} // `pub`, `pub(super)`, etc. work } fn main() { foo::m!(); // named like other items { use foo::m as n; n!(); } // imported like other items } pub use foo::m; // re-exported like other items // crate B: extern crate A; // no need for `#[macro_use]` A::foo::m!(); A::m!(); ``` - Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc. - Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used. - This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust. - Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate. - `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive). - See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89) for examples. Small example: ```rust mod foo { fn f() { println!("hello world"); } pub macro m() { f(); } } fn main() { foo::m!(); } ``` Limitations: - This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361. - Lints (including stability and deprecation) and `unsafe` are not hygienic. - adding hygiene here will be mostly or entirely backwards compatible - Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates. - pending improvements in how we encode macro definitions in crate metadata - There is no way to "escape" hygiene without using a procedural macro. r? @nrc
Just a quick comment on an old thread to say I love love love the tone and civility of this thread. Special hat tip to @withoutboats for so eloquently expressing ideas and concerns even as they were partially formed--publicly sharing ideas before they are fully formed is both very difficult and courageous. Huge respect and thanks to all--many people (myself included) are the beneficiaries of this discussion (and not just its technical content). |
Macros by example 2.0. A replacement for
macro_rules!
. This is mostly aplaceholder RFC since many of the issues affecting the new macro system are
(or will be) addressed in other RFCs. This RFC may be expanded at a later date.
Rendered
Tracking issue