[Python-ideas] Syntax for defining parametric decorators

Haoyi Li haoyi.sg at gmail.com
Mon Jul 9 04:04:14 CEST 2012


Just another point of data, Scala has exactly what is being proposed:
multiple parameter lists as synctactic sugar for nested return functions

http://stackoverflow.com/questions/4697404/scala-currying-by-nested-functions-or-by-multiple-parameter-lists


It also works with all the non-trivial edge-casey stuff python has (args,
default/named args, varargs, kwargs, splats).

My experience with this is that it actually works pretty well, the number
of "function returning function" types which require pre- or post-
processing is relatively small, and often this pre/post processing can
simply be moved into the body of the function for no real cost.

The cases where that cannot be done, it usually is because apart from
simply returning a new function, I'm also enclosing over some mutable state
in the outer function. In these cases I've found that this implicitly
captured mutable state is better handled with an object-based decorator,
making the stateful nature explicit. The lru_cache decorator seems like a
good candidate for using explicit objects to hold the cache, rather
cunningly enclosed mutable state.

I agree that as a "decorator factory helper", having extra syntax is
unwarranted. However, I think that in the general case, being able to
define curried functions in one line, a.l.a.

def func(a)(b)(c) = a * b * c

rather than

def func(a):
   def wrapper_two(b):
      def wrapper_one(c):
         return a * b * c
      return wrapper_one
   return wrapper_two

would in fact be very nice. From the Zen of Python, I would say these are
big wins for

- Flat is better than nested
- Readability Counts
- Beautiful is better than ugly

whether it's a win or lose for Explicit is better than Implicit is
debatable:

In the implementation domain, you're being less explicit about what's
actually happening (function returning functions)

but in the "how-to-use-me" domain you're being more explicit about what you
actually want (call this function with multiple parameter lists).

def f(a)(b)(c)

looks much more similar to how you would use it:

f(1)(2)(3)

than that horrible triple-nested monstrosity above.

-Haoyi

On Sun, Jul 8, 2012 at 6:46 PM, Nick Coghlan <ncoghlan at gmail.com> wrote:

> On Mon, Jul 9, 2012 at 11:11 AM, Mike Graham <mikegraham at gmail.com> wrote:
> > On Sun, Jul 8, 2012 at 6:54 PM, Nick Coghlan <ncoghlan at gmail.com> wrote:
> >> Beyond that, no, this is too limited - it only helps when there's no
> extra
> >> code in the outer scope which, in my experience, is the exception rather
> >> than the rule.
> >
> > I'm not sure I think that's the case.
>
> - functools.wraps
>
> Not really, as it uses functools.partial, not a nested def (sure you
> could rewrite it to use a nested def instead, but why?)
>
> > functools.lru_cache
>
> No, as it has pre- and post-processing steps around the nested
> function definition.
>
> - reprlib.recursive_repr
>
> Yes, this one would qualify.
>
> However, the simplest possible complete definition of decorators is as
> follows:
>
> - a decorator expression (the bit after "@") on a def statement must
> produce a function decorator
> - a decorator expression on a class statement must produce a class
> decorator
> - a function decorator accepts a function and produces another object.
> This is typically another function (or even the original function for
> annotation and registration decorators), but may also be a different
> kind of object such as a descriptor (e.g. property, classmethod,
> staticmethod) or context manager (e.g. contextlib.contextmanager)
> - a class decorator is similar, but accepts a class rather than a function
> - a decorator factory is any callable that returns a decorator
> - function decorators are often written as functions that return a
> nested function (1 level of lexical nesting)
> - function decorator factories are often written as functions that
> return a function that returns a functions (2 levels of lexical
> nesting)
>
> Creating a dedicated syntax for the special case of function decorator
> factories written to use lexical nesting makes the language as a whole
> *more* complicated rather than less. The only way to actually make
> them *simpler* would be to eliminate one or more of the bullet points
> from the above list, and that can't be done while retaining the
> current functionality.
>
> Yes, it means that to write efficient custom decorator factories you
> need to understand both the decoration process and lexical closures.
> That's *OK* - it just means writing custom decorator factories (as
> opposed to using those written by others) is a moderately advanced
> metaprogramming technique (it's still a lot simpler than writing a
> custom metaclass, though).
>
> Some concepts are just hard to get your head around, but that doesn't
> mean we should be creating dedicated syntax for a very specialised use
> case.
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> http://mail.python.org/mailman/listinfo/python-ideas
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20120708/d1ce8f89/attachment.html>


More information about the Python-ideas mailing list