Decorators with arguments are curries!

Having waded through all the decorator syntax discussions recently, and some of the historical ones, I haven't found this point mentioned before, so I'll bring it up now: Consider the following code: def foo(bar): return 1 a = foo a = foo(bar) The first assignment to a is binding a reference to a function; the second is calling the function. This is a very significant difference in python, and I'm concerned that all the current proposed decorator syntaxes[*] are liable to cause confusion on this point. For example: def foo_decorator(func): print "no params to this" return func def bar_decorator(func, param): print param return func @foo_decorator @bar_decorator("one param here") def decorated_func(): pass Here the first decorator statement is bare, while the second one includes parentheses and an argument; the first one looks like a function reference, while the second looks like a function call. I find this confusing; semantically, this appears to equivalent to (assuming expressions are allowed in decorator statements): # using curry as proposed in PEP 309 [**] @foo_decorator @curry(bar_decorator, "one param here") def decorated_func(): pass Most of my concern here is that this aspect of decorator syntax appears to be implicitly introducing a currying syntax in one special circumstance, which is then *not* transferable to currying functions in normal situations, as it would conflict with function calling. And if a currying syntax (or built-in) was introduced down the track, then these decorators would be inconsistent with them. Has anyone else noticed this issue? Andrew Durdin [*] Except those which don't allow for arguments [**] For a nice coincidence, PEP 309 suggested using '@' to mark curries

On Sat, 7 Aug 2004 22:36:59 +1000, Andrew Durdin <adurdin@gmail.com> wrote:
And that really should have been: # using rightcurry as mentioned in PEP 309 [**] @foo_decorator @rightcurry(bar_decorator, "one param here") But that's not a primary issue. The primary issues were: (1) That decorators with vs. without arguments look inconsistent, like function calls vs. function references, and (2) that consequentially, decorator syntax is implicitly introducing a non-viable currying syntax.

I think you're mistaken. When using @foo, foo should be a function taking one argument; when using @foo(x,y,z), the *call* to foo(x,y,z) should return a function of one argument (or an equivalent callable object, of course). This is often done by defining an inner "helper" function, e.g.: def funcattrs(**kwds): def helper(func): func.__dict__.update(kwds) return helper @funcattr(counter=42, obsolete=True) def foobar(): pass --Guido van Rossum (home page: http://www.python.org/~guido/)

Andrew Durdin wrote:
Correct. And that is indeed the intended meaning. Did you try this out? It gives Traceback (most recent call last): File "b.py", line 9, in ? @foo_decorator TypeError: bar_decorator() takes exactly 2 arguments (1 given) (although, as you can see, the line number is off by one) See http://www.python.org/dev/doc/devel/ref/function.html on why this is so.
And yet, the proposal does no such thing. Regards, Martin

On Sat, 07 Aug 2004 15:00:13 +0200, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Ah! Quite so. The relevant quote would be: "The result [of the decorator expression] must be a callable, which is invoked with the function object as the only argument". I guess I didn't pay quite enough attention to the examples in PEP 318 to understand them properly :)

At 10:36 PM 8/7/04 +1000, Andrew Durdin wrote:
Your example will fail, saying that bar_decorator is being called without enough arguments. Decorator syntax does *not* provide currying. You have to write something like this: def bar_decorator(param): def decorate(func): print param return func return decorate in order for your example to work.

Phillip J. Eby wrote:
Or, if the 'partial' function is in the standard lib by that point, you can use it to get your decorator. . . X>>> @partial(bar_decorator(param_val)) X... def myFoo(): pass Cheers, Nick. (P.S. Does the interactive interpreter currently do a line continuation after the decorator line?) -- Nick Coghlan | Eugene, Oregon Email: ncoghlan@email.com | USA

Nick Coghlan wrote:
(P.S. Does the interactive interpreter currently do a line continuation after the decorator line?)
Never mind. It occurred to me that my build enivironment works now, so I just went and checked what current CVS does ;) Cheers, Nick. -- Nick Coghlan | Eugene, Oregon Email: ncoghlan@email.com | USA

On Sat, 7 Aug 2004 22:36:59 +1000, Andrew Durdin <adurdin@gmail.com> wrote:
And that really should have been: # using rightcurry as mentioned in PEP 309 [**] @foo_decorator @rightcurry(bar_decorator, "one param here") But that's not a primary issue. The primary issues were: (1) That decorators with vs. without arguments look inconsistent, like function calls vs. function references, and (2) that consequentially, decorator syntax is implicitly introducing a non-viable currying syntax.

I think you're mistaken. When using @foo, foo should be a function taking one argument; when using @foo(x,y,z), the *call* to foo(x,y,z) should return a function of one argument (or an equivalent callable object, of course). This is often done by defining an inner "helper" function, e.g.: def funcattrs(**kwds): def helper(func): func.__dict__.update(kwds) return helper @funcattr(counter=42, obsolete=True) def foobar(): pass --Guido van Rossum (home page: http://www.python.org/~guido/)

Andrew Durdin wrote:
Correct. And that is indeed the intended meaning. Did you try this out? It gives Traceback (most recent call last): File "b.py", line 9, in ? @foo_decorator TypeError: bar_decorator() takes exactly 2 arguments (1 given) (although, as you can see, the line number is off by one) See http://www.python.org/dev/doc/devel/ref/function.html on why this is so.
And yet, the proposal does no such thing. Regards, Martin

On Sat, 07 Aug 2004 15:00:13 +0200, "Martin v. Löwis" <martin@v.loewis.de> wrote:
Ah! Quite so. The relevant quote would be: "The result [of the decorator expression] must be a callable, which is invoked with the function object as the only argument". I guess I didn't pay quite enough attention to the examples in PEP 318 to understand them properly :)

At 10:36 PM 8/7/04 +1000, Andrew Durdin wrote:
Your example will fail, saying that bar_decorator is being called without enough arguments. Decorator syntax does *not* provide currying. You have to write something like this: def bar_decorator(param): def decorate(func): print param return func return decorate in order for your example to work.

Phillip J. Eby wrote:
Or, if the 'partial' function is in the standard lib by that point, you can use it to get your decorator. . . X>>> @partial(bar_decorator(param_val)) X... def myFoo(): pass Cheers, Nick. (P.S. Does the interactive interpreter currently do a line continuation after the decorator line?) -- Nick Coghlan | Eugene, Oregon Email: ncoghlan@email.com | USA

Nick Coghlan wrote:
(P.S. Does the interactive interpreter currently do a line continuation after the decorator line?)
Never mind. It occurred to me that my build enivironment works now, so I just went and checked what current CVS does ;) Cheers, Nick. -- Nick Coghlan | Eugene, Oregon Email: ncoghlan@email.com | USA
participants (5)
-
"Martin v. Löwis"
-
Andrew Durdin
-
Guido van Rossum
-
Nick Coghlan
-
Phillip J. Eby