-
Notifications
You must be signed in to change notification settings - Fork 4
CS2 Discussion: Features: decorators #9
Comments
It is unfortunate that ES6 generator uses You're probably right, it's hard to parse and distinguish between the two if decorators were to keep the same symbol.
Granted, when transpiled today it doesn't make sense - So your first list item, come up with an alternative syntax for decorators, is probably the best solution. Maybe use |
Sorry, BC? EDIT: ah, Backwards Compatible. Yes, I'm increasingly coming around to that. |
Let's brainstorm other ideas...
Any other ideas? |
Regarding Btw, CoffeeScript's official docs on generator functions:
|
@carlmathisen That's a good point re Edit: whoops, this is the thread about decorators. Sorry. :) |
I've just looked at decorators in Python, Elixir and ES6. They all use In the long run, it might be a good idea to find a different shorthand for |
Although I understand the wish for a "standard" decorator syntax, I disagree with deprecation of If we decide to reserve a word or symbol for decorators, it could just as well be Example:
Btw, here is a discussion on decorators over at CoffeeScript's own issue tracker: jashkenas/coffeescript#76. Interesting read, even though the issue is quite old. |
You're probably right. 👍 Thinking about your proposal and how decorators seem to be implemented (and without having read #76), are we not be able to use decorators right now, without the keyword?
I used this type of function "decoration" before. So introducing a keyword like decorate for language supported decorators is certainly a better option than what I proposed. |
Your suggestion is basically what Jeremy says in #76 (tldr: no need for language support, just create a wrapper function) |
Ah, cool. But there might be implications that this doesn't cover. I don't know how far language support is adding additional features in ES201*. I'm pretty sure any distinction for decorators is a good thing to be able to create proper tools to use this feature. |
I know coffeescript today is against adding them but regarding getters and setters -- would decorators be used for this? If not, is it worthwhile opening a new issue and having the discussion there or is it assumed that they won't make it into this new project? Personally I really enjoy using getters in browser-side UI code. I have found it helpful to keep track of arbitrary business logic rules (a user can do this, can't do this, etc...) by writing a class DietManager {
get flags() {
let {meals} = this;
let total_calories = 0;
for(let i = 0, meal_count = meals.length; i < meal_count; i++) {
total_calories += meals[i].calories;
}
return {total_calories};
}
} At least, I think the above is a good example of a use for getters. |
Yes, please do! |
# regular square function
square = (x) -> x * x
# memo decorated square function
square = memo (x) -> x * x We don't need ES6 sugar in CoffeeScript. That's the whole point of CoffeeScript. Decorators are an awesome feature, but every language already has them. It's just that some languages make applying them awkward. Python without special syntax: def square(x): return x * x
square = memo(square) Python with special syntax: @memo
def square(x): return x * x There's absolutely no need for special syntax in CoffeeScript. If we had to use parens for invocations, it'd be different: square = memo((x) -> x * x) From experience, I can say confidently that you never need those parens in practice. You can always use the name of a decorator as if it's a prefix operator. Things are less pretty if you use functions that return decorators, but it still works well enough. You just wrap the decorator expression in parens: square = (memo 10) (x) -> x * x That could be a little bit prettier with special syntax, for example: @memo 10
square = (x) -> x * x But, it's a pretty big change to add minor convenience to a rare construct, and the new syntax would be awful when using functions as args: employees.forEach(
@memo 10
(each) -> console.log each
) That's a mess, especially when we already have: employees.forEach (memo 10) (each) -> console.log each Decorators never nest any deeper than that. You always either use a decorator, or call a function that returns a decorator (so you can parameterise the decorator). They are the only two forms decoration takes. |
@carlsmith have you used es6 decorators? The examples you provide are not valid (they can't be user on top level functions). How can one decorate a class in coffeescript with equivalent elegance to es6? |
You put the names of the decorators in front of the class. Assume Rabbit = singleton class extends Animal
constructor: (name) ->
@name = name |
There's no such thing as ES6 decorators. There's only ES6 decorator invocation syntax, so it's really just a question of what's the most elegant way to pass a function literal or class literal to another function. There's no such thing as a top-level function in CoffeeScript either. All functions are expressions. |
People need to keep in mind that a decorator is just a normal, higher-order function, like Most languages require that the arguments for any invocation are wrapped in parens, so passing a function or class literal to the decorator requires the literal to be wrapped in parens. let square = deco(function(x) { return x * x }); Many languages allow (e.g. JavaScript) or require (e.g. Python) you to use function statements. A statement is not an expression, and can not be directly passed to a decorator as an argument. You must write the function first, then pass a reference to it to the decorator afterwards, assigning the result back to the original name. function square(x) { return x * x }
square = deco(square); def square(x): return x * x
square = deco(square) In CoffeeScript, we can always prepend a decorator expression (typically a name) to a function or class literal. square = deco (x) -> x * x In other languages, they provide the decorator invocation syntax to work around limitations that CoffeeScript doesn't have (we don't have to wrap an argument in parens, and we don't have function statements). @deco
def square(x): return x * x That syntax also allows you to pipeline decorators. @deco1
@deco2
def square(x): return x * x In CoffeeScript, it's still a one-liner. square = deco1 deco2 (x) -> x * x ParametersThe @-based sugar allows you to use an invocation that returns a decorator, and decorate the new function with the result. @decoWithArg(arg)
def square(x): return x * x In CoffeeScript, you can easily do the same thing. You just need a pair of parens to keep things together properly. square = (decoWithArg arg) (x) -> x * x CoffeeScript's decorator invocation syntax works much better than employees.forEach(
@memo 10
(each) -> console.log each
) This is what we have currently:
In its defence, the @-based syntax was never designed to be applied to expressions. It's designed for function statements, which CoffeeScript correctly removed as one of JavaScript's Bad Parts™. |
Just wanted to add a massive thank you to @rattrayalex for setting this discussion up, and for moderating all of these issues. There's a lot of technical conversation taking place across a growing pool of threads, and while it's stuff we all love getting into, it must be a challenge to stay on top of everything. It's a valuable conversation that we need to have about the future of CoffeeScript, and your enthusiasm and the hard work you've put into hosting this exchange are greatly appreciated mate. |
@carlsmith I can only second that! Thanks @rattrayalex! |
Those are very good points, @carlsmith. To illustrate something like typespecs and inline (decorated) docs working already: square =
doc "squares a number and returns the result",
typespec "(number): number",
memoize (x) ->
x * x …will result in: var square;
square = doc("squares a number and returns the result", typespec("(number): number", memoize(function(x) {
return x * x;
})));
square = doc("", typespec("(number): number", function(x) {
return x * x;
})); Does look quite neat to be honest. 👍 I used higher-order functions as decorators before in Coffeescript, but sincerely thought the ES6 standard had some more features than just another invocation syntax. |
@JimPanic - Nice! I've never seen it done quite like that before. That's really cool. I'll be ripping that idea off at some point! There's a similar thing where you define a collection of functions that can take normal args or a hash that meets some criteria, and always return a hash that meets the criteria. Then you can pass args to any of the functions, then pass the result (recursively) to other functions in the collection: file = openInEditor writeToDisk newFile "foo.coffee" # process works from right to left In practice, the names will normally be shorter: file = edit set create "foo.coffee" Your example is different and more clever though still. Mixing the patterns from functional programming with the stuff we normally do with objects and impure functions never gets tired :) |
@carlsmith Thanks! Be my guest :) I used your outlined approach as well a few times; but I can't find it anymore online. I used it to describe properties for objects ( |
I must admit I haven't used decorators that much, so I'm loving the latest development in this thread. I'm definitely sold on: file = openInEditor writeToDisk newFile "foo.coffee" # process works from right to left One thing though, consider this example from http://github.com/wycats/javascript-decorators class Person {
@readonly
name() { return `${this.first} ${this.last}` }
} I've seen such class method decorators in the React community as of late. This works just fine today with CoffeeScript's classes: class Person
name: readonly ->
return "#{@first} #{last}" But how could we translate this to ES6 class methods in CoffeeScript, though? The key/value way of setting up functions in prototype objects doesn't apply in a ES6 class since it has a special function syntax like above ( |
@carlmathisen thats why (specifically the concept of a class Person
get directory_name: -> "#{@last}, #{@first}"
johnny = new Person "johnny", "appleseed"
console.log johnny.directory_name # logs "appleseed, johnny" I don't think this covers all of the options available in object descriptors though, but a property defined on an object having only the let user = {
get directory_name() {
return "appleseed, johnny";
}
}
console.log(user.directory_name) // logs "appleseed, johnny"
user.directory_name = null
console.log(user.directory_name) // still logs "appleseed, johnny" |
Some really great discussion here. Regarding the class question, for something like this: @connect(someStore)
@pureRender
export default class MyComponent extends React.Component {
// ...
} @carlsmith it sounds like you're suggesting this: export default MyComponent = connect(someStore) pureRender class MyComponent extends React.Component
# ... and one could also do this: class MyComponent extends React.Component
# ...
export default connect(someStore) pureRender MyComponent I suppose the pattern above really isn't so bad – the only downside is that it appears that all the decorators and the entire class decoration must appear on one line (testing with Any ideas how that could be resolved? Either way, I'm leaning towards concluding this thread with "no syntax sugar for decorators in CS6", with the hope that we add some helpful examples with the abovelisted patterns. |
Oh, and thanks very much @carlsmith and @JimPanic for the kind words 🙂 it's very nice to hear. If you or anyone else has feedback for how I've been conducting things so far, please reach out (anonymously, if you like, via mailbox service) - email is in my github profile. Big thanks to all those who have been contributing so much of their own time and energy thus far! |
@rattrayalex - Exactly. There's a couple of ways you can break up the longer lines when you're using decorators, but yeah, the way you did it is correct. It can get a bit wordy in some cases, as you showed, it's not unrealistic to end up with export default deco0 deco1(
class extends Animal
) If you just think of decorator syntax like... @deco
function square(x) { return x * x } ...as just sugar for... function square(x) { return x * x }
square = deco(square); ...it's kind of as though the square = deco(
function (x) { return x * x }
) From there, it's fairly obvious which different ways you could possibly express the same thing. |
sorry i was not clear i meant using the symbol notation from ruby to indicate decorators eg: class A :decorator |
@carlsmith Some very nice thoughts, thanks! I never thought about CS expressions this way. |
Hi everybody, great discussion here so far. There is, however, one small thing that hasn't seem to be mentioned that I think worth factoring in. func = # one benefit of multi liners is that you have more room for comment
decorate \ # the trick is the backslash
decorateMore(withSome, params) \ # still need parentheses here but at least they are not nested
(actual) -> body Whether that means problem solved or decorators are still to be desired, I leave it to you guys. |
Just my two cents, that decorators are awesome, and are going to be increasingly popular. Mobx being the most visible to my work, so I feel like this is a must have for CoffeeScript 2 |
Is there anything anyone wants to be able to do with decorators that requires new syntax? |
still no motion on this? Makes me feel like coffeescript 2 is DOA if it is missing features from ES7 that make it incompatible with existing libraries |
Decorators are still only a Stage 2 proposal. Any libraries that are already using them are very premature. |
I wonder if it would be possible to use backticks to pass raw javascript for the |
@light24bulbs I believe that would work, see the online CS repl: http://coffeescript.org/#try:class%20A%0A%20%20%60%40decorator%60%0A%20%20func%3A%20-%3E%0A%20%20%20 Although semicolons after decorators aren't a part of the spec, it should still work. |
Wouldn't it be great if we could just do stuff like this? It would open up a world of possibilities...
|
@aminland Cool, the syntax reminds me of API Blueprint. |
Actually makes more sense than the @ as well. I agree with the + for decorator syntax.
And just a note on unfinalized JS features. That's simply the state of JavaScript these days. If you aren't incorporating these features before they get 'finalized', you're years out of date and everyone stops using you. Node just went LTS with version 8 last month, and it has decorators. They're had it for over a year in non LTS.
There's no great risk to supporting this feature, it's going to be simple to implement, and it's a deal breaker for coffeescript if it doesn't have it.
|
one thing I'd like to add is that if we do, I really hope it'd be like python decorators which you can apply to any function, not like es6/java decorators which are restricted to methods. |
Functions are hoisted in JavaScript, that's why they didn't do it in es6.
I think coffeescript should have a 1 to 1 reproduction of decorators. Just pass the syntax right through.
|
@light24bulbs decorators do work on class level and a class is basically a function with extra semantic. |
Hey folks, A few points:
|
I've been thinking about decorators in CoffeeScript as I wanted a way for a base class to assemble a map of events to methods (for a child class) in a DRY fashion When you place anything in front of a method body in CoffeeScript, the compiler will convert it to a property on the class prototype. So I had to use class EventManager
constructor: -> @events ?= {}
via: (evt, fn) -> (@events ?= {})[evt] = fn.bind(this)
trigger: (event) -> @events[event]?()
class Task extends EventManager
completed: no
toggleCompleted: @::via 'done', ->
@completed = yes
console.log "Toggling completed!"
t = new Task()
t.trigger 'done' Just some food for thought that method decorators from a parent class will require the use of |
Migrated to jashkenas/coffeescript#4917 |
Decorators are an increasingly popular feature of ES6, particularly with React development.
However, they use
@atSyntax
, which clashes with coffeescript'sthis.
shorthand.We could:
this.
shorthandthis.
shorthandThoughts?
The text was updated successfully, but these errors were encountered: