- Notifications
You must be signed in to change notification settings - Fork2k
[CS2] Throw an error for ambiguousget
orset
keywords or function calls#4484
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Conversation
…ter/setter keywords, to warn the user to use parentheses if they intend a function call (or to inform them that `get` or `set` cannot be used as a keyword)
src/lexer.coffee Outdated
@@ -170,6 +170,8 @@ exports.Lexer = class Lexer | |||
isForFrom(prev) | |||
tag = 'FORFROM' | |||
@seenFor = no | |||
else if tag is 'PROPERTY' and prev and prev.spaced and prev[0] in CALLABLE and /^[g|s]et$/.test(prev[1]) |
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[g|s]
should simply be[gs]
.
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.
Damn. You win.
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.
For the record, that's not just shorter but also more correct.[g|s]et
would also match|et
.
Would it make sense to catch |
vendethiel commentedApr 4, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
|
The PR will also catch |
I would have said LGTM unless I had read@vendethiel's comments. Now I'm not sure. |
…ike a function or method with an interpolated/dynamic name
Okay, I’ve caught [stdin]:1:12:error:'get'cannotbeusedasa keyword,orasafunctioncallwithout parenthesesclassthenget@a=5^^^ Catching interpolated property names took some doing, but I managed to figure it out: [stdin]:1:1:error:'get'cannotbeusedasa keyword,orasafunctioncallwithout parenthesesget"a":5^^^[stdin]:1:1:error:'get'cannotbeusedasa keyword,orasafunctioncallwithout parenthesesget"#{a}":5^^^ And So my first attempt at this was to try to implement it within @vendethiel thanks for being so thorough! |
@vendethiel or@lydell, do you think you’ll have any further notes on this? I’m eager to start wrapping up the PRs for 2.0.0-beta1 (of which this is one). |
src/lexer.coffee Outdated
# Get the previous token in the token stream. | ||
prev: -> | ||
[..., token] = @tokens | ||
token if token? |
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.
I think those two lines can be replaced with@tokens[@tokens.length - 1]
- if there's no token, it'll beundefined
I tried to think of a way to detect string properties, but outside of creating a special "get/set spaced" token type that the parser would catch, I don't think it's feasible. |
…ent is a string without a colon (so a plain string, not a property accessor)
@vendethiel How about this?2d1addf |
I think we've reached the point of diminishing return, as we now error out on |
Well I should revert my last commit at least. The question is, so I also revert the support for throwing an error on |
I 👍 the latter – not worth burdening. |
@lydell do you agree? |
…st argument is a string without a colon (so a plain string, not a property accessor)"This reverts commit2d1addf.
# Conflicts:#lib/coffeescript/lexer.js
lydell commentedApr 6, 2017 • edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
Yes, I agree. |
…with dynamic property names (introduces a way to circumvent our check for trying to avoid the `get` or `set` keywords, but not worth the complications for this tiny edge case)
@lydell@vendethiel okay, I took out the check for “calls” to objects with dynamically-named properties. Care to do a final check before we merge this in? |
joallard commentedApr 1, 2020
Hey folks, I need to pitch in here: I've been running into the need for getters and setters A LOT lately. This is becoming more and more of a pain from using Coffeescript and is making me slowly reconsider that, and I'm speaking as a defender and long time user for years. The problem is that the workaround is pretty long winded, and hard to organize neatly. I've read the documentation about it probably a dozen times now, I'm still not able to learn it by heart. I haven't used the workaround once. I'm not even sure someone will read this, but I needed to document this somewhere. It seems like more and more, frameworks and libraries are offering getters and setters as the standard way to do things (I'm working with Vue at the moment). Moreover, they're a quite useful tool for a lot of cases. CS, with the experience it provides, pretty much discourages the practice. Is there really no other syntax that would work? Any workaround would be better:
anything, but please don't make me type Anyway, I don't want to rain on anyone's decisions, it's just important to me to give some feedback about the impact of this on my development experience. The reason I use Coffeescript is because it makes my experience better, and this is starting to make a dent in that, unfortunately. |
I also use Vue with CoffeeScript, and I haven't run into this issue. Perhaps you can give a realistic example? Ido find myself creating computed properties with getters and setters, but the syntax looks like this: computed: {...mapGetters'user', ['profile']token:get:->@profile?.tokenor''set: (val)->@newToken= val} The issue with You're right in that getters and setters seem to be getting more popular as frameworks use them to enable reactivity. I would be open to a shorthand syntaxif it doesn't create a breaking change. If you can think of one (play around withhttps://coffeescript.org/#try until your proposed syntax throws an error on the right) then please feel free to propose it. |
joallard commentedApr 2, 2020
@GeoffreyBooth Thanks for considering this. About your example, unless I'm mistaken, this doesn't really create computed properties. So maybe Vue just understands that magically? If so, neat. I forget my other use cases because I have terrible memory, but this time I was trying to avoid making I completely agree that Throwing a few ideas: obj= {hiddenToken:1token: .get:->@hiddenToken .set: (val)->@hiddenToken= val}"""[stdin]:4:5: error: unexpected . .get: -> @hiddenToken ^"""obj= {hiddenToken:1token::get:->@hiddenToken:set: (val)->@hiddenToken= val}obj= {hiddenToken:1token:@get:->@hiddenToken@set: (val)->@hiddenToken= val}# Same error# Other available prefixing symbols:# % ? & * =obj= {hiddenToken:1token.get:->@hiddenTokentoken.set: (val)->@hiddenToken= val}"""[stdin]:3:12: error: unexpected : token.get: -> @hiddenToken ^"""obj= {hiddenToken:1`gettoken()`:->@hiddenToken`settoken()`: (val)->@hiddenToken= val}# or something similarly literal"""[stdin]:3:3: error: unexpected get token() `get token()`: -> @hiddenToken ^^^^^^^^^^^^^"""# If I try to get creative, maybe some symbols make some deeper sense?obj= {hiddenToken:1*token:->@hiddenToken&token: (val)->@hiddenToken= val}"""[stdin]:3:3: error: unexpected * *token: -> @hiddenToken ^"""# Borrowing from C/Yaml's dereferencing operator, and Ruby setter methods:obj= {hiddenToken:1 token*:->@hiddenTokentoken=: (val)->@hiddenToken= val} I'm happy to pitch in some dev efforts if we find something that has some kind of consensus. |
I think this syntax is the one most likely to find consensus: obj=hiddenToken:1`get token`:->@hiddenToken`set token`: (val)->@hiddenToken= val Mainly because it's not actually about getters and setters at all. It's just adding another place where the One issue though is that at least in my tests, it appears that getters and setters require themethod syntax? So like this: obj={hiddenToken:1,gettoken(){returnthis.hiddenToken;},settoken(val){this.hiddenToken=val;}}; Whereas The good news is, I think that should be fine, and an improvement. If you look at classes, we already output the method syntax there; seehttps://coffeescript.org/#try:class%20A%0A%20%20a%3A%20-%3E%20123. There's been an open issue for a while to bring that syntax over to objects:#5075. So step one would be to change the output of objects to use method syntax, e.g.: # Input:obj=fn:->123 // Current output:obj={fn:function(){return123;}};// Desired output:obj={fn(){return123;}}; That should be its own PR, and would resolve#5075. Then step two would be to support a JavaScript passthrough as the method name: obj=`fn`:->123 Once that compiles (and produces the same output as if you had |
joallard commentedApr 2, 2020
That would be a huge step forward actually. So we could have: obj=val:1`get value`:->@val`set value`: (val)->@val= val compiles to obj={val:1,functiongetvalue(){returnthis.val;},functionsetvalue(val){returnthis.val=val;}}; This doesn't seem too much of a stretch fromthe current behavior. So if I understand correctly, after#5075, such a PR would be welcome? |
This PR implementsour consensus for how to handle getters and setters, or more precisely the
get
andset
keywords, in CS2:The
get
andset
shorthand syntax is too infrequently used, and a discouraged practice, for CoffeeScript to support directly. Getters and setters can be created via theObject.defineProperty
method, so they technically alreadyare supported in CoffeeScript; supporting the shorthand syntax as well just makes them more convenient to use, butDouglas Crockford argues that we should rarely if ever be using them.So the task for CS2 is to have the compiler throw an error when it appears that a
get
orset
shorthand syntax keyword is being used. Things like the following:And
set
for all of the same. These should throw compiler errors,without prohibiting people from using variables or functions namedget
orset
elsewhere in the code. Basically when a call to a function namedget
orset
is given an argument that is an object, and that function is called without parentheses, we throw an error. All someone has to do to use their function namedget
orset
is to call it with parentheses, e.g.get({b: ->})
If we someday change our minds and decide to support
get
andset
shorthand keywords, the work has already been done in this PR to recognize the tokens. They would need to be added to the grammar as keywords and output accordingly. Thanks to this PR, it wouldn’t be a breaking change.@connec@mrmowgli@carlsmith