"Real" anonymous functions for Python
lwn.net
lwn.net
something(lambda x: [
x2 := x + 10, # define variables
[ print(f"Number: {i})
for i in range(x) ], # loop
[ 0 for i in range(x)
if print(f"Number: {i}") or True
if print(f"Second line: {i}") ], # loop if you prefer "normal code order"
0 if x > 0 else {}[f'{n} should be positive'], # conditions and asserts (throws KeyError, but includes the error message)
x2 * 2 # return
][-1])
note: I'm not responsible for coworkers injuring you after committing thisCome on. No. It has the benefit of working, but definitely not being idiomatic.
Anonymous functions completely change the experience and style of programming and python is diminished for not having them.
Pythonistas I have found dismiss the importance of anonymous functions and object destructuring. I assume this is because they don’t have the experience of these things therefore do not know their value. Pythonistas point to this or that python feature saying “see, python can do javascript style destructuring” or they point to lambda functions. Again this can only mean they don’t have experience if the real thing.
There also seems to be a bit of “not invented here syndrome”, after all how could javascript (yuck!) do anything better than python?
Javascript anonymous functions and object destructuring are two of the most useful tools there are in programming and python misses out.
I saw people occasionally do weird things to bypass this, like `let x = 1 in f(x)` manually transpiled to `(lambda x: f(x))(1)` or thank god we have walrus now, so `(x := 1, f(x))` (as mentioned in the original link) is a workaround that is somewhat actually readable.
I'm not sure what style guides would declare the correct formatting to be, but this would not confusing to the parser at least:
some_function(def f(a):
return a # comma must be on the next line unless you add parentheses
, def g(b):
return b)
Note that `lambda:` and `slice[:]` are different uses of the colon, and `wal := rus` is a different token. proc some_function(a, b: proc(a: int): int) =
echo a(1)
echo b(2)
some_function(proc(a: int): int =
return a
, proc(b: int): int =
return b)
is valid Nim and compiles just fine.(Also, "1, 2" is not valid Nim, so unlike with Python you can just put the comma right after `a' without syntactic ambiguity.)
(x, y) = (y, x)
some_array[(x, y)]
Languages generally already special-case both the LHS of assignments and the inside of indexing brackets, and special-casing the RHS of assignments seems like a reasonable extension. some_function(proc(a: int): int = a, proc(b: int): int = b)
And Nim lets you put ()s in a great many places to resolve problems. I find its syntax quite a bit nicer than Python, actually.Classically trained math focused types tend to be especially bad at naming things. Which, I guess, is fine when developing the infernal equation. but it sure does not do anyone any favors when they have to understand the thing.
personally, in python, I refuse to use lambda functions on ethical grounds, all my functions are named. And then you should see my javascript, all those names would probably make any real JS dev sick.
Anonymous functions are amazing for: * Iterators (map, reduce, filter) * Callbacks (ui actions) * Threads/async tasks
The reason I don't name them isn't because I can't think of a name, it's because: * There's no point, they'll only be referred to once * Introducing a separate function block breaks the flow of the code and makes it harder to read (especially for iterators!)
Because in many cases a function is tightly associated with a particular context, and making it a separate named function is actually harmful for understanding.
personally, in python, I refuse to use lambda functions on ethical grounds
Do you really find this:
def get_name(x):
return x.name
sorted_by_name = sorted(people, key=get_name)
more readable than: sorted_by_name = sorted(people, key=lambda x: x.name) sorted_by_name = people.sort { it.name }Also due to the multiple style choices and options in JavaScript the readability is trash.
This is embarrassingly more literal than I would like, but I in large part refuse to use Python on something similar to "ethical" grounds because it doesn't support inline / multiline functions (and a couple of other similar issues).
After using typescript/ruby/groovy/scala/kotlin it's just so fused into my way of thinking about programming that each time I have to pull out an incidental function from its context and it reduces the clarity of the code, I grind my teeth. I'm guessing you just won't understand this unless you immerse yourself in another language and its idioms.
Syntax is definitely a problem, but I wonder if "looking Pythonic" needs to be such a huge roadblock.
Transpiling to comprehensions is, however, a great way to get access to arbitrary python libraries from experimental languages.
Having grown up with Python lambdas, maybe I’m blind to their limitations, but I didn’t feel like there was any real argument as to why not make a real function when you need more than a lambda.
That's missing a "not", right (probably after "really")?
Computationally, python lambda is fully capable of whatever a regular python function can do, including side-effects, (you can call foo.set_bar()).
The limit is syntactical: a lambda has to be one single expression. So, no assignment statement, no multiple lines, etc.
It works okay, but I do wish there was a better way
What's wrong with it?
This means you are either one of the best or one of the worst programmers. People in the middle have bugs in their program and can use a debugger to find them.
>sounds like a tooling issue
The tool, in this case, being the interpreter. Which is exactly the point.
In Python it’s quite common to have to refactor in order to debug.
[1]: https://python-history.blogspot.com/2009/04/origins-of-pytho...
[2]: https://github.com/amontalenti/elements-of-python-style?tab=...
from __future__ import bracesfoo = lambda x: ( z:=x+1, zz:=z*2, zz+7 )[-1]
it defines a tuple and returns the last element. List comprehensions, walrus operator and lambda make a functional language out of python.
What you currently have:
foo = lambda x: ( z:=x+1, zz:=z*2, zz+7 )[-1]
What you seem to want: foo = lambda x: (
z:=x+1,
zz:=z*2,
zz+7
)[-1]Well, there's no Pythonic way because Guido didn't let one be.
>The problem, in a nutshell, is that Python blocks are delineated using white space, not braces or keywords like begin and end, so any expression used to create a function with multiple statements would need a way to incorporate white space—or it will not look like Python at all
All kinds of other syntax made it to the language in the last 18 years, but an "end" delimiter for multi-line closures cannot?
>The problem, though, as Paul Moore pointed out, is that there is a process that needs to be followed so that the core developers will reconsider a longstanding decision of this nature; someone needs to explain why the objections raised before are no longer valid
Well, the objections raised before" are all subjective opinions about how this is unpythonic, so good luck refuting feelings.
You're either for it, or against it, and if you're for it and use subjective opinions as objections, then the other side can't really do anything, even if they state objective facts (like how developers are by now well familiar with them, how they avoid polluting the local namespace, how they're useful to setup APIs that provide the mechanism and you pass in the policy at the call site, and so on).
>But without a solution to the syntax issue, the whole argument is not worth having.
Well, there can't be a solution if the problem "we don't like how it looks" - the actual practical problem can certainly be solved.
I found them useful enough to implement a BLC interpreter [1]. Equally useful as lambda in perl, ruby, and javascript.
[1] https://rosettacode.org/wiki/Universal_Lambda_Machine#Python