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

Allow dynamic keys for object (hash) definition #3597

Closed
aurium opened this issue Aug 20, 2014 · 20 comments
Closed

Allow dynamic keys for object (hash) definition #3597

aurium opened this issue Aug 20, 2014 · 20 comments

Comments

@aurium
Copy link
Contributor

aurium commented Aug 20, 2014

This feature is available in Javascript, but not in Coffee.

opts = { s('param'): 'some value' }
# unexpected (
replacements =
  "#{prefix}keyname1": 'some value 1'
  "#{prefix}keyname2": 'some value 2'
# unexpected string interpolation
@davidchambers
Copy link
Contributor

This feature is available in Javascript, but not in Coffee.

Not true. { s('param'): 'some value' } isn't valid in either language, and opts = {}; opts[s('param')] = 'some value' works in both languages.

@michaelficarra
Copy link
Collaborator

Though ES6 will have computed keys. We should be aware of CoffeeScript's coming obsolescence if it doesn't adapt.

@lydell
Copy link
Collaborator

lydell commented Sep 10, 2014

ES6 computed keys will be available in Firefox 34: https://developer.mozilla.org/en-US/Firefox/Releases/34#JavaScript.

@akre54
Copy link

akre54 commented Oct 1, 2014

Yeah it'd be nice to have this. Either with the ES6-style brackets { [key()]: val } or a more CS-style interpolation delimiters { #{key()}: val }

@aurium
Copy link
Contributor Author

aurium commented Oct 1, 2014

I don't like @Argorate's sugar, because it looks like prototype sugar, and may cause mistakes. I believe @akre54 comes with a perfect coffeeish syntax #{...}, despite the conflict with the coffee comment.

On other rand @Argorate compatible compilation for "old" javascript is the same as i was thinking. Someone see any problem on the translation above?

myObj =
  x: 1
  #{name+i}: 'some value'   # as we can see the comment tokenization
  #{name+j}: 'other value'  # must be updated with this proposal.
  somekey: someValue + myObj.x

compiles to:

myObj = {
  x: 1
};
myObj[name+i] = 'some value';
myObj[name+j] = 'other value';
myObj[somekey] = someValue + myObj.x;

@akre54
Copy link

akre54 commented Oct 1, 2014

somekey: someValue + myObj.x => myObj[somekey] = someValue + myObj.x;

We can't just break the unquoted syntax unfortunately. That's the reason for the delimiters in the first place.

I'd rather see that input compile to:

myObj = {
  x: 1,
  somekey: someValue + myObj.x // (runtime TypeError)
};
myObj[name + i] = 'some value';
myObj[name + j] = 'other value';

For the record this is Traceur's output.

@carlsmith
Copy link
Contributor

If we're bike-shedding, can I throw in hash comprehensions again? It'd be nice if we could naturally extend the key expression syntax to comprehensions, one day.

@akre54
Copy link

akre54 commented Oct 1, 2014

That seems like a real slippery slope with some ugly unintended
consequences.

Dynamic keys are a much more modest and useful addition.

@carlsmith
Copy link
Contributor

Agreed. I just meant that it'd be worth leaving 'space' in the syntax for comprehensions, for the future, but it shouldn't get in the way of key expressions.

@MatrixFr
Copy link

MatrixFr commented Oct 3, 2014

@aurium : yes but in this use case, we will never call "prototype", so I think it's not realy in conflict, but the sugar is not realy important for me, I just hope CS will add one ;)

@lydell
Copy link
Collaborator

lydell commented Jan 4, 2015

I’d vote for "a#{b}c": value to compile to {["a" + b + "c"]: value}. Just like yield, this would be a feature that works only in supporting environments; it’s up to the developer not to use it if support for an older environment is needed. No need to a any new syntax.

@michaelficarra
Copy link
Collaborator

@lydell: I like that.

@epidemian
Copy link
Contributor

@lydell, but, unlike yield, this is a feature that can be easily implemented with ES3.

I'd vote for using the same syntax and a similar implementation than LiveScript (see "Dynamic Keys"), where:

myObj =
  x: 1
  "#{name}#{i}": 'some value' # Using string interpolation.
  (name + j): 'other value' # Using a parenthesised expression.
  somekey: someValue + myObj.x

Compiles to:

// Generated by LiveScript 1.3.1
var myObj, ref$;
myObj = (ref$ = {
  x: 1
}, ref$[name + "" + i] = 'some value', ref$[name + j] = 'other value', ref$.somekey = someValue + myObj.x, ref$);

(the output probably needs better whitespace handling)

I like the LS syntax because dynamic string literals look like strings, which is the benefit of string interpolation in the first place; and because it also allows using any value as a key (using the parentheses syntax), which in turn would be useful if we want to support Symbol properties at some point:

countdown = 
  (Symbol.iterator): *->
    yield 3
    yield 2
    yield 1

# Assuming this compiles to JS's `for ... of`
for n of countdown
  console.log n

@lydell
Copy link
Collaborator

lydell commented Jan 4, 2015

We must not forget #786 and #1731, though.

@lydell
Copy link
Collaborator

lydell commented Jan 9, 2015

Good point about Symbol keys. That means that we need to support an alternate syntax in addition to string interpolation. Both old CoffeeScript and @epidemian used (). An argument for this syntax is ease of implementation, as said in #786 (comment):

"#{key}": val

Lexer converts interpolated strings into parenthesized concatenations, so that's actually the same thing for parser.

Another option is to use ES6-style [] notation. An argument for this is: Less syntax to learn.

Which is better?

lydell added a commit to lydell/coffee-script that referenced this issue Jan 11, 2015
As discussed in jashkenas#3039 (comment).
This is the first step to implement dynamic object literal keys (see jashkenas#3597).

This also fixes jashkenas#1392.

In short, `super` is now allowed:

    # in class definitions:
    class A
      instanceMethod: -> super
      @staticmethod: -> super
      @staticMethod2 = -> super

    # in assignment where the next to last property is 'prototype':
    A::m = -> super
    A['prototype'].m = -> super
    a.b()[5]::m = -> super
    A::[x()] = -> super
    class B
      @::m = -> super
lydell added a commit to lydell/coffee-script that referenced this issue Jan 12, 2015
As discussed in jashkenas#3039 (comment).
This is the first step to implement dynamic object literal keys (see jashkenas#3597).

This also fixes jashkenas#1392.

In short, `super` is now allowed:

    # in class definitions:
    class A
      instanceMethod: -> super
      @staticmethod: -> super
      @staticMethod2 = -> super

    # in assignment where the next to last property is 'prototype':
    A::m = -> super
    A['prototype'].m = -> super
    a.b()[5]::m = -> super
    A::[x()] = -> super
    class B
      @::m = -> super
lydell added a commit to lydell/coffee-script that referenced this issue Jan 14, 2015
As discussed in jashkenas#3039 (comment).
This is the first step to implement dynamic object literal keys (see jashkenas#3597).

This also fixes jashkenas#1392.

In short, `super` is now allowed:

    # in class definitions:
    class A
      instanceMethod: -> super
      @staticmethod: -> super
      @staticMethod2 = -> super

    # in assignment where the next to last property is 'prototype':
    A::m = -> super
    A['prototype'].m = -> super
    a.b()[5]::m = -> super
    A::[x()] = -> super
    class B
      @::m = -> super
lydell added a commit to lydell/coffee-script that referenced this issue Jan 16, 2015
As discussed in jashkenas#3039 (comment).
This is the first step to implement dynamic object literal keys (see jashkenas#3597).

This also fixes jashkenas#1392.

In short, `super` is now allowed:

    # in class definitions:
    class A
      instanceMethod: -> super
      @staticmethod: -> super
      @staticMethod2 = -> super

    # in assignment where the next to last access is 'prototype':
    A::m = -> super
    A.prototype.m = -> super
    a.b()[5]::m = -> super
    A::[x()] = -> super
    class B
      @::m = -> super
lydell added a commit to lydell/coffee-script that referenced this issue Jan 17, 2015
As discussed in jashkenas#3039 (comment).
This is the first step to implement dynamic object literal keys (see jashkenas#3597).

This also fixes jashkenas#1392.

In short, `super` is now allowed:

    # in class definitions:
    class A
      instanceMethod: -> super
      @staticmethod: -> super
      @staticMethod2 = -> super

    # in assignment where the next to last access is 'prototype':
    A::m = -> super
    A.prototype.m = -> super
    a.b()[5]::m = -> super
    A::[x()] = -> super
    class B
      @::m = -> super
lydell added a commit to lydell/coffee-script that referenced this issue Feb 3, 2015
As discussed in jashkenas#3039 (comment).
This is the first step to implement dynamic object literal keys (see jashkenas#3597).

This also fixes jashkenas#1392.

In short, `super` is now allowed:

    # in class definitions:
    class A
      instanceMethod: -> super
      @staticmethod: -> super
      @staticMethod2 = -> super

    # in assignment where the next to last access is 'prototype':
    A::m = -> super
    A.prototype.m = -> super
    a.b()[5]::m = -> super
    A::[x()] = -> super
    class B
      @::m = -> super
@jashkenas
Copy link
Owner

We don't need to worry about Symbol keys — they're probably going to end up being considered a "nasty" part of JavaScript that CoffeeScript can enthusiastically avoid.

"#{key}": val

Is probably the best syntax. In an alternate universe, something like:

key: val

... might be preferable, but that ship has sailed.

lydell added a commit to lydell/coffee-script that referenced this issue Feb 7, 2015
The following is now allowed:

    o =
      a: 1
      b: 2
      "#{'c'}": 3
      "#{'d'}": 4
      e: 5
      "#{'f'}": 6
      g: 7

It compiles to:

    o = (
      obj = {
        a: 1,
        b: 2
      },
      obj["" + 'c'] = 3,
      obj["" + 'd'] = 4,
      obj.e = 5,
      obj["" + 'f'] = 6,
      obj.g = 7,
      obj
    );

- Closes jashkenas#3039. Empty interpolations in object keys are now _supposed_ to be
  allowed.
- Closes jashkenas#1131. No need to improve error messages for attempted key
  interpolation anymore.
- Implementing this required fixing the following bug:

      ("" + a): 1

  The above used to error out on the colon, saying "unexpected colon". But
  really, it is the attempted object key that is unexpected. Now the error is on
  the opening parenthesis instead.
- However, the above fix broke some error message tests for regexes. The easiest
  way to fix this was to make a seemingly unrelated change: The error messages
  for unexpected identifiers, numbers, strings and regexes now say for example
  'unexpected string' instead of 'unexpected """some #{really long} string"""'.
  In other words, the tag _name_ is used instead of the tag _value_.
  This was way easier to implement, and is more helpful to the user. Using the
  tag value is good for operators, reserved words and the like, but not for
  tokens which can contain any text. For example, 'unexpected identifier' is
  better than 'unexpected expected' (if a variable called 'expected' was used
  erraneously).
- While writing tests for the above point I found a few minor bugs with string
  locations which have been fixed.
lydell added a commit to lydell/coffee-script that referenced this issue Feb 7, 2015
The following is now allowed:

    o =
      a: 1
      b: 2
      "#{'c'}": 3
      "#{'d'}": 4
      e: 5
      "#{'f'}": 6
      g: 7

It compiles to:

    o = (
      obj = {
        a: 1,
        b: 2
      },
      obj["" + 'c'] = 3,
      obj["" + 'd'] = 4,
      obj.e = 5,
      obj["" + 'f'] = 6,
      obj.g = 7,
      obj
    );

- Closes jashkenas#3039. Empty interpolations in object keys are now _supposed_ to be
  allowed.
- Closes jashkenas#1131. No need to improve error messages for attempted key
  interpolation anymore.
- Implementing this required fixing the following bug:

      ("" + a): 1

  The above used to error out on the colon, saying "unexpected colon". But
  really, it is the attempted object key that is unexpected. Now the error is on
  the opening parenthesis instead.
- However, the above fix broke some error message tests for regexes. The easiest
  way to fix this was to make a seemingly unrelated change: The error messages
  for unexpected identifiers, numbers, strings and regexes now say for example
  'unexpected string' instead of 'unexpected """some #{really long} string"""'.
  In other words, the tag _name_ is used instead of the tag _value_.
  This was way easier to implement, and is more helpful to the user. Using the
  tag value is good for operators, reserved words and the like, but not for
  tokens which can contain any text. For example, 'unexpected identifier' is
  better than 'unexpected expected' (if a variable called 'expected' was used
  erraneously).
- While writing tests for the above point I found a few minor bugs with string
  locations which have been fixed.
lydell added a commit to lydell/coffee-script that referenced this issue Feb 8, 2015
The following is now allowed:

    o =
      a: 1
      b: 2
      "#{'c'}": 3
      "#{'d'}": 4
      e: 5
      "#{'f'}": 6
      g: 7

It compiles to:

    o = (
      obj = {
        a: 1,
        b: 2
      },
      obj["" + 'c'] = 3,
      obj["" + 'd'] = 4,
      obj.e = 5,
      obj["" + 'f'] = 6,
      obj.g = 7,
      obj
    );

- Closes jashkenas#3039. Empty interpolations in object keys are now _supposed_ to be
  allowed.
- Closes jashkenas#1131. No need to improve error messages for attempted key
  interpolation anymore.
- Implementing this required fixing the following bug: `("" + a): 1` used to
  error out on the colon, saying "unexpected colon". But really, it is the
  attempted object key that is unexpected. Now the error is on the opening
  parenthesis instead.
- However, the above fix broke some error message tests for regexes. The easiest
  way to fix this was to make a seemingly unrelated change: The error messages
  for unexpected identifiers, numbers, strings and regexes now say for example
  'unexpected string' instead of 'unexpected """some #{really long} string"""'.
  In other words, the tag _name_ is used instead of the tag _value_.
  This was way easier to implement, and is more helpful to the user. Using the
  tag value is good for operators, reserved words and the like, but not for
  tokens which can contain any text. For example, 'unexpected identifier' is
  better than 'unexpected expected' (if a variable called 'expected' was used
  erraneously).
- While writing tests for the above point I found a few minor bugs with string
  locations which have been fixed.
@pepkin88
Copy link

pepkin88 commented Feb 9, 2015

I vote for [] syntax. For majority of my use cases it's shorter by 3 characters comparing to "#{}" syntax. Also is compatible with the ES6 syntax, which means it will gain more familiarity. Moreover, I think it is more intuitive, as it's analogous to the property access syntax:

foo = { bar: 123 }
foo.bar

foo = { [bar]: 123 }
foo[bar]

foo = { "#{bar}": 123 }
foo"#{bar}" # not quite, looks like tagged template string, also doesn't work

foo = { ["#{bar}"]: 123 }
foo["#{bar}"]

If string interpolation is needed, it can still be used like this ["#{}"].

Optionally the "#{}" syntax could be implemented in addition, because they don't collide with each other.

@lydell
Copy link
Collaborator

lydell commented Feb 10, 2015

Yes, if we're adding new syntax in addition to interpolation it should definitely be []: since:

@pepkin88
Copy link

@lydell Oh I'm sorry, I didn't notice, that the "#{}" syntax is already implemented, I thought that it is still an open debate.
But still, I think the [] syntax for computed properties would be a nice addition, for reasons you stated :)

jashkenas added a commit that referenced this issue Feb 10, 2015
Fix #3597: Allow interpolations in object keys
@jashkenas
Copy link
Owner

Let's not add new syntax for dynamic keys at the moment. If we end up needing it, we can add it later.

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

No branches or pull requests

10 participants