Skip to content
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

Inconsistencies in prototype shorthand (::) and this shorthand (@) #1601

Closed
geraldalewis opened this issue Aug 12, 2011 · 97 comments
Closed
Labels

Comments

@geraldalewis
Copy link
Contributor

The :: .prototype(.) shorthand should behave consistently with the @ this(.) shorthand.

Adding symmetry in how each shorthand is implemented will give developers a better mental model of their behavior and reduce issues.

@ currently compiles to a literal this, access this. or invocation this( depending on context.
:: currently compiles to a literal .prototype or access .prototype. depending on context.
It does not compile to an invocation.

@ will not behave as :: in every context. Below is a breakdown of where they behave consistently and where they behave inconsistently.


Consistent

# JavaScript output indicated in comments
# code wrapped in -> as :: can indicate a line continuation

as value (unless :: standalone)

-> @              # this
-> C::            # C.prototype

member access (unless :: standalone)

-> @member        # this.member
-> C::member      # C.prototype.member

index

-> @['member']    # this['member']
-> C::['member']  # C.prototype['member']

applying ::

-> @::   # this.prototype
-> C:::: # C.prototype.prototype

Inconsistent

invocation

class C; C:: = (a) -> console.log a

-> @ 'invoke'     # this('invoke');
-> C:: invoke     # C.prototype.invoke; // (acts as member access)
# -> C:: 'invoke' # error (unexpected 'STRING')

list comprehensions (from #1477 by @dbrans)

for k of @ when k isnt constructor then   # for (k in this) { if (k !== constructor) {} }
for k of A:: when k isnt constructor then # for (k in A.prototype.when(k !== constructor)) {}

instanceof

@ instanceof C   # this instanceof C;
C:: instanceof B  # C.prototype["instanceof"](B);

standalone

-> @              # this
# -> ::           # error; any use-case?

standalone member access

-> @member        # this.member
# -> ::member     # error; any use-case?

Proposal

:: should:

  • still raise an error if used standalone
  • behave like @ in all other ways:
    • not indicate a line continuation
    • work in list comprehensions
    • be callable Edit: See @jashkenas comment
      (it technically is, but it looks like this line prevents implicit calls)

or

  • :: should always compile to .prototype.
  • @ should always compile to this.
  • @ should continue lines

Personally, I'm in favor of #1


Related issues: #1477, #1554 (fixed), #1234 (fixed)

@jashkenas
Copy link
Owner

Making it callable to be more consistent is one thing ... but there's never a real-world use case where your prototype object is actually a function you'd want to call ... is there? I certainly can't think of one.

@geraldalewis
Copy link
Contributor Author

@jashkenas Agreed; [I couldn't find any use-cases](http://www.google.com/codesearch#search/&q=%5C.prototype%5C(%20lang:%5Ejavascript$%20case:yes&p=6&sq=&type=cs). In fact, it might cause some confusion/issues. Updated the issue.

@michaelficarra
Copy link
Collaborator

@jashkenas: Even if it's not a common use case, it's better to allow implicit calls so that it is consistent with a regular member access. { prototype: -> }:: 0 should compile to a function invocation because { prototype: -> }.prototype 0 does and they should behave identically.

I agree with everything from @geraldalewis's (edit: original) post, including the recommendation. For the unary ::, see my proposal in #1220. I'm still in support of it.

@jashkenas
Copy link
Owner

Personally, I'm somewhat a fan of disallowing standalone @ and standalone Obj:: entirely. These inconsistencies demonstrate why.

For readability's sake, I don't know why you wouldn't always choose to write: this and Obj.prototype

@michaelficarra
Copy link
Collaborator

One more consistency worth noting:

@::   # this.prototype
C:::: # C.prototype.prototype

They can both have :: applied to them.

@michaelficarra
Copy link
Collaborator

@jashkenas: I think standalone @ or obj:: are in use all over the place, so that might cause a little bit of an uproar. But I'm really not one to care about that kind of stuff. Either way, I think they're not exactly pretty but the consistency outweighs the ugliness. If you don't like how they look, you can always use this and obj.prototype. So I think they should stay.

@aseemk
Copy link
Contributor

aseemk commented Aug 12, 2011

@jashkenas: standalone @ I've come to find really nice. It really is shorthand for this. Less keystrokes, stands out, etc. Not a big deal, of course, but it's a nice little thing I've come to like.

@geraldalewis
Copy link
Contributor Author

@michaelficarra You raise some good points. Regardless of its practical viability, the proposal calls for symmetry with @ where it's correct to do so (e.g., error on standalone ::), and a callable :: is technically correct.

ES5 Spec:

Function's prototype is a function that accepts any args and returns undefined

Callable :: is also consistent with @ (as your example points out). I won't re-edit my original post, since the strikethrough has been referenced, but I'm once again in favor of a callable ::.

@jashkenas I favored standalone @ over this for a bit before I too came to the conclusion that this is more readable. I felt I was writing in the idiom of CoffeeScript by using standalone @. Ironic since there's such focus on readability in CoffeeScript. I understand why you'd want to lose them. I do like them, and find them to be readable, in loops k for k of @, k for k of C::

Also, another inconsistency:

@ instanceof C   # this instanceof C;
C:: instanceof B  # C.prototype["instanceof"](B);

@satyr
Copy link
Collaborator

satyr commented Aug 13, 2011

Note that @pthis.p doesn't always hold:

$ bin/coffee -bpe '{@p}'
({
  p: this.p
});

$ bin/coffee -bpe '{this.p}'
Error: Parse error on line 1: Unexpected 'THIS'
...

$ bin/coffee -bpe '{@::}'
Error: Parse error on line 1: Unexpected '::'
...

@geraldalewis
Copy link
Contributor Author

@satyr That's interesting. What was the rationale for not allowing

{this.p}

?

@michaelficarra
Copy link
Collaborator

@satyr: Nice catch. We should make it so.

@geraldalewis: #1089. It really should be allowed.

@rstacruz
Copy link

rstacruz commented Sep 1, 2011

Also, {a.b: c} doesn't work as well.

@satyr
Copy link
Collaborator

satyr commented Sep 1, 2011

{a.b: c} doesn't work as well

What's the expected compilation?

@rstacruz
Copy link

rstacruz commented Sep 1, 2011

@satyr, oops, that was never valid JS to begin with. Sorry about that, just assumed it was.

@michaelficarra
Copy link
Collaborator

@geraldalewis: what's the status of this issue? Are you still planning to work on it? I'd love to see this all fixed.

@geraldalewis
Copy link
Contributor Author

I'm still in favor of proposal #1 (I'm again in favor of making :: callable, though I'd redacted it earlier):

:: should:

  • still raise an error if used standalone
  • behave like @ in all other ways:
    • not indicate a line continuation
    • work in list comprehensions
    • be callable

I haven't moved forward with a patch because it didn't look like it had strong support (@jashkenas voiced misgivings about having standalone @ at all). Though maybe until that point the @'s fate is decided, it'd be helpful to have it and :: act consistently?

#1089 (Accesses in LHS & RHS destructuring assignments) still feels like its own issue -- do you agree? Or was that the issue you'd like to see fixed.

@satyr
Copy link
Collaborator

satyr commented Sep 6, 2011

#1089

@jashkenas: ... we should remove {@foo}. The reason why it's there is because @foo is treated like an identifier, grammar-wise.

Sounds like we're removing it. And with that logic, (@name) -> should be disallowed as well.

@jashkenas
Copy link
Owner

Yep, I'm afraid I still have strong misgivings about it.

not indicate a line continuation

:: is a close cousin of ., but instead of referring to a property on an object, you're referring to a property on the prototype of an object. If you can write:

object.
  property.
    value

you should equally be able to write:

object::
  property.
    value

be callable

Again, :: should feel like . when you use it. If you can write a. b to mean a.b, then a:: b should mean a::b in the same fashion.

@geraldalewis
Copy link
Contributor Author

you should equally be able to write:

I think this is where standalone-@'s inconsistencies appear again:

this.
  property #this.property;

@
  property #parse error

If you can write a. b to mean a.b, then a:: b should mean a::b

Again, there's an inconsistency with standalone-@:

this. a    # this.a;
@ a        # this(a);

@jashkenas
Copy link
Owner

Yes, and that's why standalone @ is the unsatisfactory bit of syntax to blame here. On both readability grounds and inconsistency grounds.

Does it want to mean this or does it want to mean this.? If we choose the latter, that implies getting rid of standalone @.

@geraldalewis
Copy link
Contributor Author

Now I get it. Even if we patched the inconsistency between @ and ::, @ would still be inconsistent with this. (in some contexts) and :: would then be inconsistent with o.prototype.. "Fixing" the inconsistency would create more inconsistencies... Ouch.

In favor of removing standalone-@s now (which would also resolve this issue).

@jashkenas
Copy link
Owner

A good change for a 1.2 release? Let's do it.

@michaelficarra
Copy link
Collaborator

So I'm still not quite clear on the status of this discussion. Does this mean we're getting rid of both standalone @ and postfix unary ::?

@jashkenas
Copy link
Owner

Yes please. Let's keep the sigils limited to the cases where the trailing dot makes sense. this. and .prototype., respectively.

@michaelficarra
Copy link
Collaborator

Okay, I'm cool with that. It makes sense. There's going to be some unhappy users though. Can't wait for the backlash...

@525c1e21-bd67-4735-ac99-b4b0e5262290

+1

We support the removal of ambiguity and thus welcome change. Although this one is particularly bitter-sweet since we have @ all over the place :(

At least it's reasonably easy to replace.

Now would be a great time to sanction a single option for the other ambiguous parts of CoffeeScript.

@devongovett
Copy link

Totally in favor of these changes. I never used standalone @ anyway preferring this in that case for readability. I only used @ when accessing members or calling functions. As for Obj::, that just looks funny, and yes, I think for consistencies sake it should act like a . as @jashkenas stated above.

+100

@geraldalewis
Copy link
Contributor Author

sanction a single option for the other ambiguous parts of CoffeeScript.

Any issues in particular come to mind?

@525c1e21-bd67-4735-ac99-b4b0e5262290

(Object.keys foo).length

vs

Object.keys(foo).length


do foo

vs

foo()

@jashkenas
Copy link
Owner

Sure thing, Object.keys(foo).length is canonical, but the former will always continue to work.

@TrevorBurnham
Copy link
Collaborator

OK, so there are clearly a very wide range of opinions here (even just on the issue of the standalone @). Given that, I think the prudent thing to do is to avoid making the language more restrictive and wait for stylistic standards emerge organically. Until then, folks can use @ or this freely as they prefer.

Sound reasonable?

@arbales
Copy link

arbales commented Oct 26, 2011

Late to this party… but… the issue came up in one of our pull requests, so…

I agree with @showell and @michaelficarra that we should think of @ as sometimes being used in a contraction. This allows @ to mean this without being inconsistent.

What that doesn't really do is justify the removal of the standalone @ — which does look pretty lonely all-by-itself. In the end, it seems like we can rationalize removing or keeping lone @'s till the end of time. They do appear everywhere, though, and that should count for something. If they are to be removed, I think the decision would be more stylistically motivated than anything else. Not that there's anything wrong with that! 💅

Now, if only this were named self……

@artyomtrityak
Copy link

Really i don't understand standalone @ issue.

We have @ alias for this. We know how it works. We always use @ when mean this.

To be consistent we should always use @ instead of this.

So we have 3 choices:

f = ->
  @
f = ->
  this
f = ->
  return @

imho

  1. the worse. We should not use this because coffee gives us @ - it's shroter and cleaner.

  2. Is the best - it's clear and short. In my projects it works and looks okay

  3. we know that coffee returns last statement so return @ can be used only for readability improvement

@vendethiel
Copy link
Collaborator

@artyomtrityak The last comment on this issue was 2 years ago,
I agree with you that bare @ looks okay - a lot of people don't.

@michaelficarra
Copy link
Collaborator

I removed standalone @ support from CoffeeScriptRedux in michaelficarra/CoffeeScriptRedux@31ad76f. CoffeeScriptRedux now respects both outcomes of this issue: no standalone @ and no postfix ::.

peyerluk referenced this issue in livingdocsIO/livingdocs-engine Sep 3, 2013
Add and remove classes according to the values in SnippetView.model.styles. Style changes trigger SnippetTree#snippetHtmlChanged events which are handled in the Renderer.
emmenko referenced this issue in commercetools/sphere-node-sdk Jan 23, 2014
@testbrian
Copy link

A lot of people do like the bare @. An inconsistency with :: doesn't justify not using one of the hallmarks of coffeescript

@otse
Copy link

otse commented Oct 8, 2015

I have an extended class Klas, and I want Klas to use its own method foo, not the override-foo

I achieve this with Klas::foo.call this, param. Good form ye?

@GeoffreyBooth
Copy link
Collaborator

Skimming through this thread, I don’t see a consensus on any action to be taken as a result of the original proposal. If I’m mistaken on this please comment and I’ll reopen, but at this point if anyone desires further changes to :: it’s probably best to start a new issue with a concise proposal concretely defined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests