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

Decorators #76

Closed
weepy opened this issue Jan 11, 2010 · 11 comments
Closed

Decorators #76

weepy opened this issue Jan 11, 2010 · 11 comments

Comments

@weepy
Copy link

weepy commented Jan 11, 2010

Was looking at Python for ideas to plunder. Not sure about the usefulness of them, but they looked interesting, and as they are syntactic sugar, they might be relevant. Here's the relevant section in Wikipedia:

A decorator is a Python object that can be called with a single argument, and that modifies functions or methods. Python decorators were inspired in part by Java annotations, and have a similar syntax; the decorator syntax is pure syntactic sugar, using @ as the keyword:

@viking_chorus
def menu_item():
    print "spam"
is equivalent to
def menu_item():
    print "spam"
menu_item = viking_chorus(menu_item)
Decorators are a form of metaprogramming; they enhance the action of the function or method they decorate. For example, in the above sample, viking_chorus might cause menu_item to be run 8 times for each time it is called:
def viking_chorus(myfunc):
    def inner_func(*args, **kwargs):
        for i in range(8):
            myfunc(*args, **kwargs)
    return inner_func
@jashkenas
Copy link
Owner

You're starting to be a real one-man research lab, weepy, this is great stuff. Don't mind the opinion below:

The idea of doing Python's decorations in CoffeeScript/JS is a total abomination. A decorator hands a precompiled method over to another class or method, for it to wrap it or rewrite it at will with metaprogramming, before being replaced back into the original source. All of it seems like a hack around the fact that Python doesn't have convenient first-class functions, and 95% of the use cases seem to be well handled by the notion of wrapping a function in JavaScript.

Take a look at the PythonDecoratorLibrary, and take a look at Underscore's implementation of "wrap". Most of the decorator patterns -- memoization, logging, function retry, counting calls, currying, dumping arguments, default functionality, smart deprecation warnings -- can be cleanly implemented with a wrapping function in CoffeeScript, and the rest of them (like synchronization) don't apply to JavaScript.

That's my two cents. Replace the function with a wrapped version and you're good to go.

@weepy
Copy link
Author

weepy commented Jan 11, 2010

This makes sense. I wasn't really suggesting them seriously, just saw them and figured they looked interesting

@jashkenas
Copy link
Owner

Yeah, quite, and good to have on the record. Thanks for bringing it up. Closing the ticket.

@rowbee
Copy link

rowbee commented Jun 28, 2010

I would like to discuss the idea of decorators a little please.

When you say "replace the function with a wrapped version" how would the code appear?

add: (val) ->
  decorator: (fun) ->
    decorated: (x) ->
      fun(x) + val
    return decorated
  return decorator

Multi-wrapping without decorator sugar:

mysquare: add(3) (add 2) (add 1) (x) ->
    x*x

Multi-wrapping using a decorator sugar:

@add 3
@add 2
@add 1
mysquare: (x) ->
    x*x

I think using decorators reads more like a DSL, eliminates some parens, it is easier to see how the function is wrapped, and it is easier to comment out a single decorator line to disable it.

You could call decorators an abomination, but wrapping functions in functions is such a useful idiom that having syntactic sugar makes it easier to read and write for the programmer.

Please help me to understand perhaps with syntax examples: what is the downside of decorator sugar?

@jashkenas
Copy link
Owner

I'm not sure what to say other than to repeat myself. To wrap a function in another function, you just pass it in:

decorator func

It's as simple as that. It's also not all that common of an idiom (in JavaScript). The vast majority of the time, it's simpler to accomplish what you want by:

  • Having the function perform or check for the decoration behavior at its start.
  • Pass in the decoration behavior as an argument to the function.
  • Simply write a new version of the function that does what you want.

Modifying a function from the outside should be reserved for rare circumstances -- it's spooky action at a distance, and diverts important code from being located in the proper position. Instead it's slapped on like a fresh coat of paint.

It's necessary in Python because functions aren't real lambdas... So let's take an example from one of the canonical ones in the Python wiki -- a state machine:

http://wiki.python.org/moin/PythonDecoratorLibrary#StateMachineImplementaion

@on_leave_function(tstate)
def _leave_tstate(self):
    print "leaving state ", self.turtle.name() , "of ", self.mname

Simplifying, and doing it in CoffeeScript, we get:

tstate.onLeave ->
  print "leaving state ${@turtle.name} of ${@name}"

I think that's a real improvement.

@rowbee
Copy link

rowbee commented Jun 29, 2010

That is great example where decorators are not needed in CS, but there are situations where decorators are more concise:

What is the best way to rewrite this web framework style code w/o decorators?

@app.require 'admin'
@app.cache 600
@app.route '/blog'
posts: (request, response) ->
    response.write('Blog')

Would you rewrite it like this?

posts: app.require('admin') (app.cache 600) (app.route '/blog') (request, response) ->
    response.write('Blog')

Or would you do something like this?

posts: (request, response) ->
    response.write('Blog')
posts: app.route('/blog') posts
posts: app.cache(600) posts
posts: app.require('admin') posts

@jashkenas
Copy link
Owner

What do you think of this?

route '/blog', (request, response) ->
  return unless @admin request
  response.write 'Blog', {cache: 600}

Here's another hypothetical example, assuming a filter setup:

filter '/blog', adminRequired

route '/blog', (request, response) -> 
  response.write 'Blog', {cache: 600}

@rowbee
Copy link

rowbee commented Jun 29, 2010

The first example looks really nice, but it makes assumptions that the permissions are coupled directly into the handler via @admin, and assumes that write accepts a {cache: 600} argument (who said write knows anything about caching?).

The second example seems to repeat '/blog' twice, so is not as DRY.

Decorators could be an abomination, but I think in some cases they are the most DRY and expressive syntax, because you can cleanly compose third-party and orthogonal functionality.

Further off topic...

What if CoffeeScript was modular in the sense that people could write a tiny syntax plugin to add a specific syntax feature. So someone could, for example, write a tiny decorator plugin, and then simply require that plugin with CS before running the compiler. So if you were writing a web framework with CS, you would select a base set of syntax plugins to be loaded before compiling to JS. New language features could start as plugins and some could mature and become core, that way people could extend the compiler via small plugins...

@jashkenas
Copy link
Owner

rowbee: CoffeeScript has a long and sordid history of language extensions ... We're currently discussing this ticket in #coffeescript, if you want to stop by and argue the point.

@denisw
Copy link

denisw commented Oct 13, 2015

I know this issue has been closed years ago, but your decision to not include decorators might need to be reconsidered in the light of newest developments in the framework space. Several frameworks, such as Angular 2 and React-related libraries such as Alt or Redux, now make extensive use of decorators in their APIs for declaratively enhancing classes.

Of course, support for decorators is not strictly needed - they can still be called as functions. But requiring developers to do so is awkward, and I feel like it's not in line with CoffeeScript's vision of being a pleasan no-fuzz syntactic sugar layer above JavaScript.

@rstuven
Copy link

rstuven commented Oct 13, 2015

@denisw This could be helpful in the meantime: https://github.com/rstuven/es-decorate

This issue was closed.
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

5 participants