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

RFC: #[attribute]s galore #2602

Open
wants to merge 7 commits into
base: master
from

Conversation

Projects
None yet
6 participants
@Centril
Copy link
Contributor

Centril commented Nov 26, 2018

🖼️ Rendered

📝 Summary

Permit attributes to be attached to lifetimes, types, bounds, and constraints as well as associated type equality constraints Foo<#[attr] Assoc = MyType>. For example, in a hypothetical version of proptest one may write:

#[proptest]
// #[quickcheck] could also work similarly.
fn addition_commutes(
    // Run this test for (u8, u8), (u16, u16), and (u32, u32);
    // This is interpreted by `#[proptest]`.
    (a, b): #[types(T, u8, u16, u32)] (T, T)
) -> bool {
    a + b == b + a
}

// Accept x: u8 and vec: Vec<{ y: u8 | y > x }>.
// This is then verified by an external tool.
fn refined(x: u8, vec: Vec<#[require = "> x"] u8>) { ... }

💖 Thanks

To @petrochenkov and @alexreg for their reviews of the draft proposal.

Show resolved Hide resolved text/0000-attributes-galore.md Outdated
Show resolved Hide resolved text/0000-attributes-galore.md Outdated

@Centril Centril referenced this pull request Nov 28, 2018

Open

Meeting 2018-11-28 #15

2 of 8 tasks complete
@l4l
Copy link

l4l left a comment

I do like the feature itself, it is very powerful from one hand, especially for the amount of boilerplate that can be reduced. From the other I concerned about the possible code complexity, since the code might become really cryptic (as in your artificial example). So I would like to ask about more use-cases particularly for the following list of grammar position (names from example):

  • orange_juice
  • icecream/upsilon/omega
Show resolved Hide resolved text/0000-attributes-galore.md Outdated
@alercah

This comment has been minimized.

Copy link
Contributor

alercah commented Jan 2, 2019

Your #[linear] example involves a procedural macro which expands to nothing so that an external tool can understand it. But that requires the external tool to operate on pre-macro expansion code, doesn't it, to find the #[linear] attributes? What if the #[linear] attribute is introduced by another macro, how will the tool detect it?

@Centril

This comment has been minimized.

Copy link
Contributor

Centril commented Jan 3, 2019

@l4l

From the other I concerned about the possible code complexity, since the code might become really cryptic (as in your artificial example).

I think artificial is the operative word here. The example referenced is for demonstrating new capabilities, I don't think any real code would even come close to that level of attribute density.

So I would like to ask about more use-cases particularly for the following list of grammar position (names from example):

orange_juice

type Beta = #[size = "< 42"] Vec<#[orange_juice] Alpha>;

The use cases are the same as for examples: #[types(T, u8, u16, u32)], Vec<#[require = "> x"] u8>, #[repr(C)] { foo: u16, bar: u16 }, and etc. This falls naturally out of being able to annotate types with attributes. Another use case is conditionally taking away applied types to some type constructor, e.g. MyType<u8, #[cfg(foo)] u16>, which can be useful if the second position to exist on some platforms for example.

omega

Same as for #[orange_juice], in particular wrt. cfg.

upsilon

The idea and motivation is the same as for #[eta], #[theta], and #[chi]. Instead of having to conditionally compile an entire function to add or remove a certain bound, we can simply conditionally add the bound itself by writing Foo: #[cfg(bar)] Baz + Quux. If bar does not hold, then you get Foo: Quux.

icecream

This is primarily done because once you admit #[omega] then admitting #[icecream] gives a more consistent experience. IOW, if you can, for the sake of conditional compilation, write Foo<'a, #[omega] 'b>, then allowing &#[icecream]'a T is extending attributes to lifetimes not just in type constructors but also in general. As for concrete use cases of #[icecream], one can imagine encoding extra information, that cannot be said in the type system, for being more clear in conjunction with unsafe.

@alercah

Your #[linear] example involves a procedural macro which expands to nothing so that an external tool can understand it. But that requires the external tool to operate on pre-macro expansion code, doesn't it, to find the #[linear] attributes? What if the #[linear] attribute is introduced by another macro, how will the tool detect it?

As always with macros, they have certain limitations due to solely dealing with syntax. However, the tool could 1) operate on pre-macro expansion code, and 2) at the same time run its own expansion engine and if #[linear] is found in expansions it will collect the places it occurs. Really, a tool can do whatever it wants, including being a fork of a rust compiler. This is not particular to the position in which #[linear] occurs, but to macros in general. Indeed one could imagine writing:

#[linear(ImportantType)]
fn foo(x: ImportantType) {
    // ERROR! We didn't use `x` as promised.
}
@graydon

This comment has been minimized.

Copy link

graydon commented Jan 12, 2019

Opposed. Cognitive and implementation costs exceed putative benefits. The syntax was never meant to be nested or generalized. Should remain special case.

@Centril

This comment has been minimized.

Copy link
Contributor

Centril commented Jan 12, 2019

@graydon

Cognitive and implementation costs exceed putative benefits.

Could you elaborate? What cognitive costs do you foresee here for someone who has already learned the attribute syntax? The implementation costs as far as I can see in libsyntax do not seem particularly complicated to me as compared to the benefits. You say putative, why? Is there some reason you believe the benefits not to be actual? Is there some specific instance, where attributes are now allowed, that you find more objectionable than others in this proposal? Are they all equally objectionable?

The syntax was never meant to be nested or generalized. Should remain special case.

I don't think meant to is particularly interesting here. It has been generalized multiple times afaik because people have found it useful to do so to satisfy real world applications. Therefore "remain" is not entirely accurate. It can only not become more general.

@graydon

This comment has been minimized.

Copy link

graydon commented Jan 12, 2019

They are presently restricted to several special cases. IMO they should remain as such unless strong evidence motivates pushing them into new classes.

The cognitive load comes from the user running into code that uses the new productions in ways they had not seen before, that they previously didn't have to encounter / parse / elaborate in their head. See the term "cryptic" used in the first comment above. In general: having a more complex language to read. The proposal even says as much: "This proposal complicates the grammar of Rust".

I expect you'll respond by an appeal to uniformity or consistency here, as the user now doesn't have to remember the special cases where attributes are or are-not allowed; to which I can only say: consistency and uniformity of that sort does not make a language easier on users. Languages that let you put everything in every context are not easier for humans, they're harder. The reader gets fewer fixed points of reference in their comprehension of the text, less can be predicted or assumed, they have to work harder because there's more room for different productions to still be meaningful. Consistency in the sense of not adding meaningless variation is good for comprehension, sure; consistency in the sense of generalizing rules of combination to allow more legal combinations than necessary is bad for comprehension.

Re: "putative": motivation case 1 is on a structural record type which I've argued elsewhere shouldn't exist. Motivation 2 is for nonexistent static analysis tools that might someday exist. Motivation 3 is from a multiplexing procedural macro in the introductory example plainly labeled as "hypothetical". I think it's fair and maybe even a bit strong to call these cases "putative".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment