jashkenas / coffeescript

Unfancy JavaScript
https://coffeescript.org/
MIT License
16.5k stars 1.98k forks source link

Allow dynamic keys for object (hash) definition #3597

Closed aurium closed 9 years ago

aurium commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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

lydell commented 10 years ago

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

akre54 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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 commented 10 years ago

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

Dynamic keys are a much more modest and useful addition.

carlsmith commented 10 years ago

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 commented 10 years ago

@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 commented 9 years ago

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 commented 9 years ago

@lydell: I like that.

epidemian commented 9 years ago

@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 commented 9 years ago

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

lydell commented 9 years ago

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 https://github.com/jashkenas/coffeescript/issues/786#issuecomment-484874:

"#{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?

jashkenas commented 9 years ago

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.

pepkin88 commented 9 years ago

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 commented 9 years ago

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

pepkin88 commented 9 years ago

@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 commented 9 years ago

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