-
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
Turn capitalization conventions into language rules for 1.0 #154
Turn capitalization conventions into language rules for 1.0 #154
Conversation
local binding otherwise. This context-dependent meaning presents a significant | ||
vulnerability to mistakes and accidental breakage. The convention of writing | ||
`enum` variants and `static`s uppercase and local bindings lowercase, if it is | ||
adhered to, eliminates this vulnerability, but it is only a convention. |
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.
But there are other conventions which could also prevent such mistakes, right? So a lint seems appropriate since we are guiding the user to a particular convention, but as long as they follow some convention, they will be OK.
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.
The idea is that guarantees are valuable. A convention is not a guarantee, especially not unless you can ensure that Rust code using one convention will never have to come into contact with Rust code using a different convention.
The motivation is just not at all convincing. The match issue has far better (i.e. non-stylistic) proposed solutions (e.g. rust-lang/rust#4639 for the variant/variable ambiguity, I'm sure there have been suggestions how to resolve the static variable ambiguity (e.g. disallow them in matches)). If this is really a big enough wart to warrant such drastic solutions, then the wart should be fixed with a more limited solution. Additionally, this doesn't work well with FFI and automatic binding generators. |
Yeah, I find the FFI argument the most convincing. You really don't want On Fri, Jul 4, 2014 at 4:43 PM, SiegeLord notifications@github.com wrote:
|
Please, no. this is bad enough.
|
👎 I don't like the idea of semantic case. Go has it bad enough in using case as a public/private distinction, but using it to define different classes of identifiers that are valid for use in different contexts seems far worse. Also, how is this supposed to handle |
👎 for named-parameter function calls with braces. Ridiculously inconsistent.
👎 for syntactic differences between fn calls and tuple structs. It's enough that the identifiers are unique. 👎 for grandfathering. This isn't Java, there's no need to riddle ourselves with inconsistencies so early in the game. We can easily change to 👎 for turning builtin types into keywords. This isn't C++ with its ridiculous
No, for the same reason you can't use a
In that proposal, it's shown that lifetimes are unambiguous anyway, if they are moved before the |
It seems to me that people are reacting to this idea on a visceral, emotional level, which I don't really understand. My guess is that it's because of the alienness of the idea in the context of most mainstream languages. But if people want to exclaim, "it's bad enough in $LANGUAGE", the example they should be using isn't Go, but Haskell. They do exactly the same thing (as what I'm proposing, not as Go), and it works pretty well for them. Haskell is a smart language in many ways. I'll try to address the individual arguments.
I'm not even sure how that could work. If the identifier has the name of an enum variant, then it's not a variable, therefore the lint doesn't apply. If it's a variable, then it doesn't have the name of an enum variant, therefore the lint doesn't apply. Even if I'm missing something, it seems like this would only catch a subset of the cases.
I tried suggesting this previously a couple of times, and naturally, people objected. I've come around to agreeing with them. The reason is that
I think it's a pretty big wart. (And I don't think this is a drastic solution, especially as fixing the wart is only one of the reasons I think we should do it.) And, again, Haskell runs into the exact same issue and solves it the exact same way. But if you can come up with a more limited solution that people are willing to accept, you will have my everlasting gratitude.
Me too! But not convincing enough. Broken record at this point, but Haskell does the same thing, and they're doing fine. Furthermore, automatic binding generators seem especially well suited to automatically, mechanically, and predictably adjusting identifiers where necessary.
Did you read the proposal? @alexchandel
In theory, I agree with you. In practice, if you want to submit another RFC proposing to break every Rust program in existence for purely superficial reasons, be my guest. It will gain 100 comments in a day, and go nowhere. I'm surely not going to do it.
Once again, the existence of an alternative solution which people are also unwilling to accept is not a persuasive counterargument. (And the lifetimes thing was, like the others, just an example, not a proposal.) |
Any desirable features that result from a proposed change provide support for that change. "Being able to distinguish classes of identifiers based on their capitalization opens up significant room for adding new features to the language" isn't a convincing argument for mandatory capitalization if the new features aren't particularly desirable, or if mandatory capitalization isn't necessary for them. |
The logic wasn't that we should make the change because then, we can do the things in the list. It was that, if I could think of five ideas while I was writing the proposal, there are probably lots of other situations where the same capabilities might come in useful. |
You might want to add a section on dealing with FFI. In C you may have a
|
@matthieu-m Perhaps. :)
I don't think
I think we already have this as
|
identifiers. Thus, they are legal as identifiers for anything in the | ||
uppercase-class, not just types, and not for anything in the lowercase-class. | ||
|
||
3. Turn them into keywords. |
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.
Or rename them to Int, UInt, I8, U8, etc.
I personally think that syntax is Haskell's weakest point.
This lint would apply to cases like this: use foo::A; // whoops, forgot to import all the variants
mod foo
{
pub enum Bar
{
A,
B,
C
}
}
fn main()
{
match A
{
A => (),
B => (), // A variable that would trigger the lint
}
} Which is the by far most common way you hit this ambiguity.
I don't disagree that it's natural, but not all natural things need to be supported if they have a high potential for error. It doesn't seem terrible to me to force doing this with static variables: match 1
{
x if x == STATIC_VAR => ()
} Pretty much 100% of my usage of this feature comes from transducing C enums into Rust enums, so I wouldn't be burdened by this (the FFI-Rust bridge always looks a bit ugly). I certainly prefer this to enforced capitalization.
I strongly dislike all those ideas, so in my opinion, the fact that all these ideas are enabled by this RFC is an argument against it. Giving more freedom to language designers is the worst thing you can do, from my POV. Giving this freedom because you've introduced some non-discoverable rules into the language is even worse. |
Some thoughts:
To consider under the 'Alternatives':
|
This is precisely the wrong way to do it. Instead, you would want to have one class of identifiers that are forbidden from starting with a lowercase character, and one class of identifiers that are forbidden from starting with an uppercase character. This allows you to entirely sidestep the issue of Unicode compatibility, and, assuming that Unicode identifiers remain behind a feature flag, also allows you to retain the "adding new features backwards-compatibly" benefits mentioned elsewhere. That said, I am against this proposal. The FFI argument is too strong to ignore. Pointing out that it works for Haskell is not convincing; Rust cares much more about cleanly interfacing with C than Haskell does. I'm also not convinced that the other benefits mentioned are worth much. The proposed syntax for named function arguments is quite a stretch. I'm not convinced that indexing into tuples is useful, and even if we wanted it I don't see why we wouldn't just use the existing indexing syntax (or why we would re-use the I do think it's important to have an idiomatic Rust style, which is why I'm grateful for the current lints, and I'm happy that they're warn-by-default instead of allow or deny. A hardline stance doesn't pull its weight here. |
Despite my negativity, I do want to thank you for taking the time to make a well-written RFC. If only all our bikeshedding were so thoughtful. |
-1. Personally, I don't find pro arguments convincing (examples like optional arguments really do look weird), and FFI argument against it is very strong. I also think that case enforcement is just wrong and is just unnecessary limit for the programmer. Lint is more than enough for it. |
@glaebhoerl Although I don't think it should be mandatory, adherence to the standard style should probably be encouraged as much as possible, to avoid the potential bugs the RFC describes. Why not add a styler/formatting tool to rust? Go has |
@bstrie: I keep seeing mentions of the FFI issue, and I am afraid I do not see the problem. It seems it is possible to have a name for Rust, and a link name for linking, and therefore it does not matter if one cannot use the exact same identifier in Rust than in the target language. However, I see an issue if the style is NOT uniform: tooling. For example editors can be configured to help you follow a certain style, in general, by pointing out irregularities or even automatically clean up behind you. If the style is almost uniform (except for some FFI items), then those editors will have to (somehow) understand the FFI rules which requires being able to look-up the relevant identifiers in their module to see whether they are FFI items or not, etc... this goes way beyond a simple styler/formatter! And requires either re-implementing libsyntax or managing to link with it from whatever language the styler/formatter is written in... if it is at all possible (not purely declarative). |
I suspect this is the underlying sentiment in most instances, and we're all just trying to come up with more sophisticated rationalizations. This is not a ding; I do it too; it comes with being human. That said, I wish that, along with saying "I don't like it", people would try harder to locate the true source of their dislike. Maybe we can learn from it. Maybe I'll even find it convincing. Your dislike is noted, but it's not an argument. To me, doing this sounds much more like a free lunch than like a freedom-trampling restriction. We're already following these rules in the vast majority of cases. All we need to do is to formalize them, and then we get various benefits for free. (And one more time with feeling, the benefit is not being able to do any of the five particular things I chose to throw out as examples, but in general the ability to distinguish certain kinds of identifiers from one another syntactically.) I acknowledge the FFI issue, but I don't think it rises beyond the level of an annoyance. You need two rules:
Automatic bindings generators can apply these rules automatically, and consumers of the bindings can apply the rules in their heads when recalling the names of things. It's not rocket surgery.
Could you explain why?
I see. Thanks. (I wonder if we could have variants come into scope along with the
I tried floating this as recently as three weeks ago. According to pcwalton, "I used to argue that guards were sufficient, but the community pushback was overwhelming and consistent. People really really expect to be able to match on constants." And on top of that, (a) I now agree that it should be possible, and (b) removing it doesn't even solve the problem, because the same issue exists for
I do think we should keep brainstorming alternative solutions, even if I don't think the odds are good. For instance, allegedly Old Rust used to require writing
but somehow, I suspect that any proposal to require more syntax in
Fair enough, but then we're back to dislike.
Interesting idea, but wouldn't this defeat the purpose? The purpose is to be able to guarantee that certain kinds of things can never have the same name. If neither-uppercase-nor-lowercase characters are allowed in all cases, there's no longer a guarantee.
This is like when people try to impress C++ programmers by showing that in Rust,
will fail to compile. And the C++ folk shrug and say, "So? Every C++ compiler gives a warning for that. It's a solved problem." And the Rustic folk put their hands to their heads, because they know it's bigger than that, but they don't see how to communicate it to the people from C++. I really, really don't like saying to people, "We've left some landmines in the language... but don't worry! You can reduce your risk of stepping on them if you use this lint and that convention.", when we have it within our power to not plant any landmines. The point is that guarantees are valuable. It means not having to do due diligence, or ever think about "well, what if...". To the question, "What are the chances of that ever happening?", I want to be able to answer, "0%". |
The naming conventions have exceptions. I don't like the rigor of language rules concerning capitalization. Better disallow shadowing a valid name with one that doesn't follow conventions: enum Bar {
Foo(int) // follows the convention
}
fn main() {
fn Foo(_: int) {} // should cause an error in this specific example
println!("{}", Foo(1));
} |
@matthieu-m I put in a bit about FFI.
Why?
What would this accomplish? |
Note that I used this argument to resist adding this feature, but it was constantly asked for. |
@pcwalton I too find it a little odd that statics are allowed to be used in I think Swift may have actually gotten this one right, forcing all enum variants to be scoped under the enum and allowing the scope to be inferred from type so you can just say But of course it's a bit too late to be making a sweeping change like that to Rust. In the meantime, if statics in patterns are an issue, maybe we should change it so references to statics must be prefixed with the keyword match foo {
Some(static STATIC_VAR) => stuff(),
_ => bar()
} Not that I'm seriously suggesting we make this change; as I said before, statics are pretty much the same case as nullary enum variants, and we can't solve the latter without much more sweeping changes. |
I see nothing wrong with people rejecting this because of mere 'dislike'. Subjective reasons are omnipresent in language design.
This just means that the issue (the ambiguity between variables and variants/static variables) is not a big enough problem to those people to make the pain of these solutions worth it. Personally, I am perfectly content in leaving this wart in because I think it's not a big wart as evidenced by this type of sentiments. |
I'm a hopeless idealist, but I would hope for RFCs to be discussed using meaningful arguments grounded in reason, with at least the theoretical possibility of a participant being persuaded by another's arguments, and decided on the merits. Even in discussions on truly superficial aspects of the language such as whether to keep the And subjective is fine, but "I don't like it" is not even a reason. It's a conclusion.
I think this sort of complacency and optimism is ill-advised. "Well, it's not really a problem in practice" and "it hasn't bitten us so far, so it should be fine" are the kind of sentiments which later tend to go down as famous last words. A few years from now, I think this is likely to be regarded as one of the few unfortunate mistakes of an otherwise exceptionally well-designed language. |
I think the "it hasn't bitten us so far" argument is quite reasonable, given that an entire compiler is written in Rust, and "so far" spans a period of several years. |
The entire compiler is written by people who have the most intimate familiarity with the language that anyone's ever going to have, and who have adopted a set of informal rules for capitalization to avoid getting bitten by it, which are the same rules that this proposal seeks to formalize. "If it hasn't bitten you yet, it's going to bite you later" is something like an iron law in programming, especially when "later" involves doing things at larger scales than previously. And that's again something like the whole justification for Rust's rigorous type system, which seeks to make entire classes of errors impossible, instead of merely trying to make them less likely. I don't see why the same mentality shouldn't be applied to the rest of the language. |
IMO this is much better as warnings rather than errors. There's translations of legacy interfaces, e.g. I much prefer gl code to look like gl code anywhere else; and supposedly theres foreign languages where it doesn't apply. I suspect rust lets you syntax highlight by context a lot more already ... :Type ... Trait for Type.. as Type ... etc. So the capitalisation rule is just something subjective |
@glaebhoerl There is actually a lot of code in the compiler that currently violates these rules. |
This is too kind. As far as I can tell the feedback has been not just mostly but almost uniformly negative, with a couple of people seemingly neutral. To recap where this stands from my perspective: People have identified two issues which I think are real, but not very big. Most commonly cited was FFI. As noted in the RFC, this seems easy to resolve by just having bindings generators apply a mechanical transformation such that foreign identifiers which don't follow Rust's capitalization rules simply get their capitalization adjusted so that they do. This doesn't sound like the end of the world, and I don't think it would be more than an annoyance. The other was unicode identifiers (which are not currently legal) and alphabets without a case distinction. Again, I think we could come up with ways to resolve this when we reach that point (for instance, by saying that such characters always count as uppercase and must be prefixed with On the other hand, doing this would fully resolve a very real and in my opinion troubling semantic wart with respect to name resolution in patterns, which seems very difficult to resolve any other way, would open up greater space for backwards-compatible language changes in the future, and would not have much of a real cost, as the large majority of existing code already follows these rules. As far as I can tell, most people seem to be standing on principle for their right to name their identifiers however they like, even if they rarely end up exercising it. The other logical underpinning of the proposal, alongside that it has benefits and not much of a cost, is that it could be rescinded backwards-compatibly after 1.0, should we choose to, but not added. As such, it may be prudent to add the rules before 1.0 just to keep our options open, if there's even the possibility that we might want to have them. A potential compromise here would be to just put the use of non-conformant identifiers behind a feature gate, instead of banning them outright, which would be easier to reverse should we decide that the rules aren't carrying their weight (in effect, change them from opt-in with the current lints to opt-out). But if opposition to the idea is universal and there's basically no chance of it being considered desirable, then it's probably better not to bother. |
A big -1! A bad idea to distinguish public items or not! Unacceptable. |
@liigo This has nothing to do with public items, I'm not sure what you're thinking of. Also, could you please be a bit more detailed and constructive? This isn't a shouting match. Thanks. |
I am actually really pro making this lint into a language rule, personally. I just haven't said anything because I knew this thread would end up really noisy. |
Thanks for taking the time to write up this RFC! This was discussed at today's triage meeting and the decision was to close this for now. Rust has trended largely towards C in many of its design decisions, including having no restrictions on the format of identifiers. At this time we would like to continue that trend and not turn these into language rules but rather retain the middle ground of having a default-warn lint for rules such as these. |
FWIW, this is a reasonable argument and I've been waiting for someone to make it. |
Nice to see it was closed. I don't like capitalization conventions. |
@liigo You established your stance up above. If everyone who had opposed to a particular RFC chimed in after closure with a "thank goodness!", it would be a lot of noise. Such behavior would also be rude (and thus in violation of the Rust community code of conduct). Such curt dismissal is quite disrespectful of the effort put in by the RFC author. I assume that you do not mean to be rude, but I also ask that you consider more carefully whether the terse comments you post are actually adding value to the discussion; otherwise all they add are negative vibes. If you have questions, or do not understand the feedback I am providing, I recommend you chat about it directly with me or another core team member, via email or on IRC; my IRC nick is |
Sorry!
|
stream::iter accepts IntoIterator
Pretty