-
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
Unify and sweeten the syntax for attributes and macros using the @foo
notation.
#208
Conversation
Unresolved question: IdentMacro? |
That's a good point. It'd be |
Sometimes there really feels like there should be a parser evaluator that can take a grammar and tell you k for LL-k and SLL-k, and other key metrics. So not |
How would the inner/outer attribute distinction be made? I'm uneasy about macros and attributes having the same syntax. It complicates the parser and could also complicated a developer's visual "parsing" of the file. |
The inner/outer distinction would be made the same way as today; e.g. |
@bharrisau |
@sfackler macros and attributes having the same syntax strikes me as a good thing, particular since decorators like |
I meant for The '[]' does feel a little redundant for the attributes as the brackets need to match anyway (the |
@nikomatsakis I'm a bit worried about situations like this ending up being confusing: @do_a_thing()
struct Foo {} The RFC states that EDIT: It could also be a bit weird that a normal item definition can't be followed by a semicolon, but a macro expanding to item definitions must be followed by a semicolon. |
I like things the way they are now. Macros and attributes may both be user-definable syntax extensions, but that doesn't mean they're the same thing. That's why macros are called macros, and not attributes. I view attributes as metadata, and macros are not metadata to me. Because of this semantic difference, I'd like the syntax for macros and attributes to be different. |
That's precisely what's confusing, IMO. Metadata to me means "data about data". But attributes are code: they actually describe a transformation, just like a macro. They are in fact precisely the same thing as a macro, just with different syntax. |
@sfackler That's consistent with what we require for other items that use |
@sfackler Note that the proposal matches the current setup for things like tuple structs versus other items. |
If the syntax are to be changed, I'd prefer They are sometimes related (so the notations also have |
@pcwalton, under the hood, yes they are all code transformations, but they are used differently. Attributes are modifiers that "describe" some aspects of another piece of code, while macros are self-contained and don't describe anything else. |
I understand that there can be differences, but I don't see that this warrants a separate sigil any more than than e.g. pure functions versus impure functions would deserve a separate sigil. |
would syntax like @deprecated="foo" be whitespace dependant? could we do
if not, i vastly prefer @deprecated("foo")
Actually I prefer the latter anyways :) +1 for this RFC |
Sorry my finger slipped, I agree that we don't need two sigils, but having some distinction is better. If we see macros/attributes as compile-time functions, then macros are freestanding ones while attributes are "methods" with an implicit first arg that is the modified struct/enum/module/crate etc. |
This is going to turn into a bikeshed very quickly; before it does, let's point out that Let's try to refrain from "I like X over Y because X looks better" comments. |
+1 for the |
@sinistersnare, I think @sfackler @pcwalton, I prefer changing And one more reason why I think macros should be |
|
@CloudiDust I expect that attributes will eventually permit almost anything inside their body, for what it's worth. (It'd certainly be useful to be able to provide arbitrary token-trees as input to |
Isn’t I’d rather we just keep what we have now, but change macros to use a leading |
@nikomatsakis, that can be a great feature. But it is still true that macros are "standalone" and many can be used in the context where (runtime) functions are expected, like While attributes are not standalone. They "modify" some nearby piece of code (or the module/crate) and are not used like runtime functions. |
@P1start That's a good point, but inner attributes can still be used inside blocks. We used to use |
@P1start, FWIW, Rust used to have prefix |
@kballard, I think the problem is that macros and functions share the same syntax when they are actually different (they execute differently). Lisp has simpler surface syntax rules, at the cost of increased difficulties when telling different things apart. That's why Clojure added "non-lispy" things that are "more syntax rules", and I like them. That's also one of the reasons why I am against using |
But then again, Lisps have simple cores and much of the essential functionality is implemented with macros. And requiring something like |
@kballard That ignores both that lisp macros can have non-s-expr forms (loop, format, read macros) and that other non-s-expr langs have sigil-free macros. |
This represents a very large ambiguity, as it would have to tokenise as |
If macros and attributes share the same syntax, how could one know if I want |
+1 unify. +1 @deprecated("foo") |
+1 @kennytm. Macros/attributes could potentially be allowed in more locations in the future, like attributes on block expressions. This would limit the places they could be used. |
6357402
to
e0acdf4
Compare
I still don't understand the motivation for unification. Some attributes happen to be macros and they can share implementation details. So what? Even in a hypothetical unified future, everyone would still refer to 'attributes' and 'macros' as distinct things used for distinct purposes. If their similarity isn't general enough to obviate one term or the other, why should it be considered general enough to obviate one syntax or the other? You say they are two sides of the same coin. But one side of a coin is called heads and the other tails, and they get different pictures. Distinctions are useful. |
@nikomatsakis I am now quite fond of the one solution proposed by Sodel_the_Vociferous in the comments of your blog post. I expended on his comment a bit:
The colons at the end of outer attributes signify the fact that an outer attribute applies to the immediately following non-attribute entity. We can also later support macro/outer attribute/inner attribute arrays: |
Please, keep the hashtag form for attributes. It is nice. |
As the plan is to almost unify macros and attributes, and maybe allow macros to be used like attributes, we should indeed put macros and attributes in the same namespace, or at least forbid macros and attributes with identical names. I think we can see Currently, macros can only be called with But in the future some macros will be callable with Anyway, "calling" an attribute feels weird, maybe we should just start calling them "decorators" and draw parallels with Python then: A Python decorator is a function that gets called with the decorator syntax A Rust decorator is a compile-time function (in other words, macros or other compile time magic) that gets called with the decorator syntax Python doesn't have a "compile time" and doesn't have macros, so it can simply use So it is fine for Rust to have three related but distinct calling notations for macros/outer decorators/inner decorators. |
@nikomatsakis: After reading your blog post, I am leaning towards option 3, specifically because it will maintain a clear distinction between attributes and macros. Having both of them being represented the same will leave the distinction "on convention basis" and from my experience, that's not a good thing, especially when reading code, (remember, code is much more read than it's written). |
Using the @!feature(some_fancy_feature)
@![some_fancy_attr, another_fancy_attr]
@deprecated("This is old fashioned."):
@[repr(C), deriving(Show)]:
struct MyOldVector {
x: f64,
y: f64,
}
@deriving(Show):
enum MyVector {
Vec2(f64, f64),
Vec3(f64, f64, f64)
}
@inline:
fn create_random_vector() -> MyVector { ... }
@macro_rules here_be_magic { ... }
fn main() {
match create_random_vector() {
res @ Vec3(_, _, _) => @println("The result is a 3D-vector: {}.", res),
@cold: Vec2(x, y) if @here_be_magic(x, y) == 6 => @println("Lucky!"),
Vec2(_, _) => @println("The result is a 2D-vector {}.", res)
}
} The other parts look nicer than my original proposal ( And in this regard, |
For what it's worth, a attribute syntax like
|
👍 Two points for using
|
As someone who's written a lot of macro invocations I have a really strong "ughhhh" gut reaction to
The benefits of freeing up We should also consider the interaction with some other bits of proposed syntax:
The first one seems like a wash (I mind the |
Hm, I’m starting to like this RFC more and more. (Although I still prefer But I can’t get over the ambiguities this introduces. For example, // Calling a macro invocation
(@foo(bar))(baz)
// A statement macro followed by a parenthesised expression
@foo(bar);
(baz)
// An attribute with one parameter decorating a parenthesised expression
@foo(bar) (baz)
// An attribute with no parameters decorating a call expression
@foo (bar)(baz) From the RFC itself, today’s semantics, and the previous discussion around ambiguities, I gather that the second interpretation would be how the parser would parse it, but any of the others could be the intended interpretation. |
I think we can close this as "not gonna' happen". |
Summary
Unify and sweeten the syntax for attributes and macros using the
@foo
notation.Motivation
Currently, attributes and macros/syntax extensions are both conceptually macros: they are user-definable syntactic extensions that transform token trees to token trees. However, their syntaxes are quite different: at present, attributes use
#[attr()]
, while macros usemacro!()
. By switching to a uniform syntax,@attr()
and@macro()
, we can emphasize their similarity and save syntactic space. At the same time, we reduce the verbosity of attributes and make them more like other languages by using the@
notation.The
!
and#
notation take up syntactic space that we may want to use elsewhere. For example, RFC #204 suggests using!
for error assertion.At least the following languages use
@
notation for attributes: Java (annotations), Python (decorators), D, Dart (metadata), Scala (annotations), and Swift. Languages have generally chosen either@
or[]
(brackets) to represent attributes; we cannot choose the latter because of ambiguity with array literals.Julia uses
@
for macros.Objective-C uses
@
to indicate special notation not in the C language. Since Objective-C is not a macro expander but is a full-fledged compiler, this is not directly analogous. But, in the author's opinion, the@
sigil has a similar feel in Objective-C.Detailed design
The following syntactic changes occur:
#[inline]
→@inline
#[inline(never)]
→@inline(never)
#[deprecated="May discolor some fabrics"]
→@deprecated="May discolor some fabrics"
println!("Hello {}", "Niko")
→@println("Hello {}", "Niko")
vec!["spam", "eggs", "bacon"]
→@vec["spam", "eggs", "bacon"]
bitflags! { flags Flags: u32 ... }
→@bitflags { flags Flags: u32 ... }
Parsing is slightly complicated because, where an item is expected, the parser does not know whether an item macro or an attribute is next after parsing the leading
@
, identifier, and(
. Therefore, the parser parses a series of parenthesis-delimited token trees in these cases, and looks at the next token following the)
to determine whether it parsed an item macro or an attribute. If the next token is;
, it considers what it just parsed an item. Otherwise, it reinterprets what it just parsed as an attribute.The special rules to handle Unix-style
#!
can be simplified, because it may never be parsed as an attribute.Drawbacks
@
vis-à-vis#
/!
is in the eye of the beholder.Alternatives
There are innumerable other syntaxes one could consider. Unadorned brackets for attributes, C#-style (e.g.
[inline(never)]
), does not seem possible to reconcile with our array syntax.The impact of not doing this is that the current syntax will remain.
Unresolved questions
@deprecated="foo"
may be ugly. Should we do anything about this? One possibility is to switch to@deprecated("foo")
, which is more consistent anyhow.