Chris Angelico writes:
On Wed, Jan 6, 2021 at 9:27 PM Steven D'Aprano <steve@pearwood.info> wrote:
I'm glad that things like Perl one-liners, obfuscated C, and sewerage treatment works exist...
:)
+1
Multi-statment anonymous functions are, in my opinion, overrated, and a (slight) code smell. If your lambda is so complex it requires more than a single expression, shouldn't it have documentation and tests?
That's a great exaggeration. Less so now that we have the conditional expression, comprehensions, and genexps, but still.... In Lisp, you can actually substitute a defun for a lambda because the defun returns a callable (a symbol similar to a Python function object). "It's a bold strategy, Cotton." Not sure it would pay off in Python. But see below.
There are some other use-cases, where something is syntactically an anonymous function, but in practice, it's a part of some larger construct. In Python, a lot of those use-cases are handled by some form of function decorator or maybe abuse of class notation, but I wouldn't rule out the possibility that there are cases where those are suboptimal.
Consider UI building, for instance. You need to have event handler functions, and you need to build your UI. There are a few possibilities:
1) Magic names within a class. If you have a thing called "btn_frob", and a function called "btn_frob_clicked", then that function will automatically be called when the button is clicked. 2) Objects have function decorators as attributes, so "@btn_frob.clicked" will define the function to be called when the button is clicked. 3) Name all your event handlers and place them out of line, allowing you to use simple function parameters to associate them. 4) Define a dict with attributes, and use lambda functions to handle events eg {"clicked": lambda: ...}
Nothing's perfect, and nothing ever will be, but you can see how option 4 would become more viable with multiline lambdas. The function can definitely be anonymous,
*Any* function can be anonymous. Just copy the lambda around as you need it. Inline for efficiency. ;-) The question is the tradeoff between availability of a name *in multiple contexts* and the annoyance of choosing one. In the case in point, you'd have to depend on the UI builder class to associate the event name with the anonymous handler for debugging, for example. Suppose that lambda had this syntax: lambda PARAMETER-TUPLE [NAME]: DEFINITION Then you could use a list: [lambda (context) clicked: ...]. Note that this list doesn't need to pollute the namespace (and in that sense would be anonymous). The lambda would know its own name, and the builder or the runtime would fish that out. (I can see a number of reasons why this "fishing" design would not be great, and the system might duplicate the name in an attribute for straightforward access. But I suspect that's also true of the dict design.) I am specifically suggesting that the presence of the NAME parameter gives lambda's DEFINITION the syntax of def.
testing and documentation apply to the entire UI rather than to any specific function (it's generally useless to try to unit-test a single event handler function outside of its context),
This is inaccurate in both directions. First, you generally don't need the whole UI. You need the context, which is a compact representation of the history of the UI's behavior. Second, the need for context is present any time you don't have a pure function, which in Python practice is most of the time.
and most importantly, defining it out of line separates relevant and related concepts.
There may be better examples, but I think that UI is not a good one here. For all practical purposes, a UI element is a collection of event handlers. A series of defs with descriptive names is more than sufficient to associate the related concepts. Collect them in a list, hand it to the UI, and then let the UI format them as it pleases.
an ugly multiline lambda won't be better than the alternatives,
The multiline lambda can't be worse than my suggestion above, which is just a def that returns the function object without associating it with any name in the enclosing namespace. Are defs so ugly?
[If] anyone comes up with a really elegant syntax, I'd love to discuss it and help with PEP authorship.
Before anyone asks, I'm not interested in authoring this PEP. ;-) Steve