Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[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

Merged
GeoffreyBooth merged 10 commits intojashkenas:2fromGeoffreyBooth:get-set-warning
Apr 9, 2017

Conversation

GeoffreyBooth
Copy link
Collaborator

This PR implementsour consensus for how to handle getters and setters, or more precisely theget andset keywords, in CS2:

Theget 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 aget orset shorthand syntax keyword is being used. Things like the following:

classAgetb:->c=getd:->e=->getf:->

Andset 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 supportget 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

…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)
@@ -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])
Copy link
Collaborator

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].

Copy link
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Damn. You win.

Copy link
Collaborator

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.

@vendethiel
Copy link
Collaborator

Would it make sense to catchclass then get @a = 5? (adding properties to the prototype).

@vendethiel
Copy link
Collaborator

vendethiel commentedApr 4, 2017
edited
Loading

get "a": 5 will compile toget({a: 5});, andget "#{a}": 5 compiles toget({[`${a}`]: 5});.

@vendethiel
Copy link
Collaborator

The PR will also catch{get a: b} = c, but I'm fairly sure that's fine :) (on master, it desugars it to{get({a: b})} = c, and error onget).

@lydell
Copy link
Collaborator

I would have said LGTM unless I had read@vendethiel's comments. Now I'm not sure.

@GeoffreyBooth
Copy link
CollaboratorAuthor

Okay, I’ve caughtget andset before static methods:

[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{get a: b} = c is supposed to be caught.

So my first attempt at this was to try to implement it withinnodes.coffee’sclass Call, but I realized there was no way to determine if a call was “spaced,” or a function call without parentheses, withinCall. That info is known to the lexer and not passed on. So either we need to pass thespaced property onward for everyIDENTIFIER token so that it can eventually be read byCall, which seems like a rather major change and potential performance impact, or we do this check entirely within the lexer and we need to work around the fact that we don’t yet know that a particularIDENTIFIER might eventually become aCall. Anyway it seems like we actually can catch all the cases we need to (or can think of) just within the lexer, so it looks like I got lucky.

@vendethiel thanks for being so thorough!

@GeoffreyBooth
Copy link
CollaboratorAuthor

@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).

# Get the previous token in the token stream.
prev: ->
[..., token] = @tokens
token if token?
Copy link
Collaborator

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

@vendethiel
Copy link
Collaborator

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.
So LGTM, looks good, thank you.

…ent is a string without a colon (so a plain string, not a property accessor)
@GeoffreyBooth
Copy link
CollaboratorAuthor

@vendethiel How about this?2d1addf

@vendethiel
Copy link
Collaborator

I think we've reached the point of diminishing return, as we now error out onget 'item: 1'. I don't think there's a simple and unambiguous solution.

@GeoffreyBooth
Copy link
CollaboratorAuthor

Well I should revert my last commit at least. The question is, so I also revert the support for throwing an error onget 'a': ->, string properties? I guess the safest is to leave that as is, and the parentheses are required for aget call to any string; or we can just overlook string property names as an edge case not worth burdening allget calls with strings.

@vendethiel
Copy link
Collaborator

I 👍 the latter – not worth burdening.

@GeoffreyBooth
Copy link
CollaboratorAuthor

@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
Copy link
Collaborator

lydell commentedApr 6, 2017
edited
Loading

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)
@GeoffreyBooth
Copy link
CollaboratorAuthor

@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
Copy link

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:

{property.get -> ...}# or{`get property`: -> ...}

anything, but please don't make me typeObject.defineProperty!

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.

@GeoffreyBooth
Copy link
CollaboratorAuthor

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 withget andset is that they're not reserved words; it's possible to have a function namedget and to call it, e.g.get(). So CoffeeScriptget foo already has a meaning:get(foo). We can't change that without it being a breaking change, and seeing as how commonget is as a function name (see Express, for example) that would be a pretty disruptive change.

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
Copy link

@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 makingobj.value() into a function call and keep it asobj.value. The module callingobj.value was elsewhere and I was concerned with maintaining the interface of a property. Basically, this is a case where one needs to keep it as a property (getter) call.

I completely agree thatget should be kept useable as it is already. I can see the use. I would thus suggest an alternative syntax. It being ugly I wouldn't mind that much, as long as it's not as contorted as it is now. I can think of a few things, you folks will have more experience than me at understanding what it means for the CS codebase and proposing improvements.

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.

@GeoffreyBooth
Copy link
CollaboratorAuthor

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 thePASSTHROUGH_LITERAL token is supported in the grammar. We should probably do this even if wealso invent some new syntax for shorthand getters and setters.

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;}};

Whereasget token: function() is invalid JavaScript syntax. This is an issue because currently CoffeeScripttoken: -> compiles totoken: function() {}. We would need to change the output format for object properties that are functions to use the method syntax, and we would need to do that all the time, since CoffeeScript doesn't know what's inside a passthrough block.

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 hadfn without backticks) then you're done. Users could replacefn withget fn and get the output they want.

@joallard
Copy link

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?

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

@vendethielvendethielvendethiel left review comments

@lydelllydelllydell approved these changes

Assignees
No one assigned
Projects
None yet
Milestone
2.0.0
Development

Successfully merging this pull request may close these issues.

4 participants
@GeoffreyBooth@vendethiel@lydell@joallard

[8]ページ先頭

©2009-2025 Movatter.jp