Shorthand syntax for lambda functions that have a single parameter

Lambda functions that have a single parameter are a common thing, e.g. for "key" functions: `sorted(items, key=lambda x: x['key'])`. For these cases however, the rather long word "lambda" together with the repetition of the parameter name, results in more overhead than actual content (the `x['key']`) and thus makes it more difficult to read. This is also a difficulty for `map` and `filter` for example where there's lots of visual overhead when using a lambda function and hence it's difficult to read: `filter(lambda x: x > 0, items)` or `map(lambda x: f'{x:.3f}', items)`. Hence the proposal is to add a new syntax via the new token `?`. For the examples above: * `sorted(items, key=?['key'])` * `filter(? > 0, items)` * `map(f'{?:.3f}', items)` The rules are simple: whenever the token `?` is encountered as part of an expression (at a position where a name/identifier would be legal), the expression is replaced by a lambda function with a single parameter which has that expression as a return value, where any instances of `?` are replaced by the name of that single parameter. For example: * `?['key']` translates to `lambda x: x['key']` * `? > 0` translates to `lambda x: x > 0` * `f'{?:.3f}'` translates to `lambda x: f'{x:.3f}'` * `?*?` translates to `lambda x: x*x` With the difference that the replacement function would use an unnamed parameter, i.e. not shadowing any names from outer scopes. So `? * x` would use `x` from enclosing scopes, not as the lambda parameter. Regarding operator precedence, the rules would be similar as for lambda functions, i.e. it will include as much in the expression as it would for a lambda function. To find more example use cases, I used `grep -rnE 'lambda [_a-zA-Z][_a-zA-Z0-9]*:' Lib/` on the CPython standard library to find single parameter lambda functions and there are various results coming up (although not all are applicable, e.g. because they return a constant value). I'll include a few examples below, but the actual list is much longer: ``` modes[char] = max(items, key=lambda x: x[1]) modes[char] = max(items, key=?[1]) # proposed new syntax obj = unwrap(obj, stop=(lambda f: hasattr(f, "__signature__"))) obj = unwrap(obj, stop=hasattr(?, "__signature__")) s = re.sub(r"-[a-z]\b", lambda m: m.group().upper(), s) s = re.sub(r"-[a-z]\b", ?.group().upper(), s) members.sort(key=lambda t: (t[1], t[0])) members.sort(key=(?[1], ?[0])) ``` Of course, there is no requirement to use `?` as the token, it could be any other character that's currently not used by Python, i.e. `$` or `!` would be possible too.

Chris Angelico wrote:
You are right, I didn't think of this ambiguity w.r.t. to the start of the expression. While `lambda` clearly marks where the lambda body starts, `?` doesn't do this. Actually the above example could also be translated to `lambda x: members.sort(key=(x[1], x[0]))` (which doesn't make sense, of course, but it's valid syntax).

I find this approach too cryptic compared to reading regular Python notation, my brain has to mode switch to make sense of it. Would a little extra ?: be too much add to make clear it's a lambda function, e.g. ?: ? > 0 instead of ? > 0 Also either approach *could *add multi-argument lambdas: ?1, ?2: ?1 > ?2 or just ?1 > ?2 I wonder though if there's a nice approach to have a no argument lambda? 🤔 Damian (he/him) On Wed, Sep 29, 2021 at 8:55 AM Dominik Vilsmeier <dominik.vilsmeier@gmx.de> wrote:

IIUC a lot of what is being discussed has been implemented by the "placeholder" package on PyPI Here: https://pypi.org/project/placeholder/ It allows using things like `min(data, key=_[-1])` or `_.age < 18` (just using language features instead of adding new syntax).

On Wed, Sep 29, 2021 at 09:11:35AM -0000, Dominik Vilsmeier wrote:
I think those are massively more cryptic and hard to read than an explicit lambda. Being too terse is worse that being too verbose: consider how few people use APL compared to how many use Java. "lambda" is admittedly an odd name for a concept, most familiar to computer science theorists, but at least it is pronouncable in English. We can read it and talk about it: "Pass a lambda as the key parameter." Whereas here how do we pronounce "?"? "Pass a question mark as the key parameter" sounds like *all you need do* is pass the literal ? symbol. In practice people are going to call it lambda, in which case you might as well just write lambda. And please take mercy on the poor beginners who have to Google for "Python ?" to try to work out what on earth this does. At least other punctuation marks are either well known from school maths or other languages.

Given that we have comprehensions that use s simple expression, and operator.itemgetter for common keys, the use cases for these simple lambdas are pretty rare these days. Sure, some folks seem to prefer map and filter as a matter of style, but I don’t think we need to create cryptic notation to make that easier, NOTE: I expect many of the examples you found in the stdlib predate comprehensions and itemgetter. -CHB On Wed, Sep 29, 2021 at 8:09 AM Steven D'Aprano <steve@pearwood.info> wrote:
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On 2021-09-29 10:11, Dominik Vilsmeier wrote:
[snip] I'd prefer something like "x -> x" "x => x" as an equivalent to "lambda x: x": sorted(items, key=i -> i['key']) The advantage of using '->' is that it already exists. This has all been suggested before. There have also the suggestions of adding a None-coalesce operator '??', plus None-coalesce subscripting with '?[...]', etc, which would be Python's equivalent of the null-coalesce operator of some other languages.

On 2021-09-29 10:11, MRAB wrote:
I'd prefer something like "x -> x" "x => x" as an equivalent to "lambda x: x":
I continue to find all such syntaxes less readable even than lambda. The idea of using a hyphen and a greater-than sign to "draw" an arrow doesn't sit well with me. The only problem with lambda is that the word "lambda" (unlike other Python keywords) is unusual and its relation to its function obscure. If we're going to change something, we should just come up with a new keyword like "shortdef" or (perhaps with these new parser possibilities I've heard vague references to) just allow "def" to be used for lambdas somehow. To my eye, adding arrow-like syntax doesn't help anything. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

Dominik Vilsmeier writes:
Hence the proposal is to add a new syntax via the new token `?`.
I find that unreadable. If you're going to add new syntax, either bite the bullet and just allow 'λ' for 'lambda' (my preference as an old lisper), or 'x → f(x)' (following MRAB's earlier suggestion). Or maybe omitting the function name from a def: 'def (x): f(x)'.

Over in typing-sig we're considering a new syntax for callable *types*, which would look like (int, int, str) -> float. A matching syntax for lambda would use a different arrow, e.g. (x, y, z) => x+y+z. On Wed, Sep 29, 2021 at 11:51 AM Stephen J. Turnbull < stephenjturnbull@gmail.com> wrote:
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

On Wed, Sep 29, 2021 at 02:09:03PM -0700, Guido van Rossum wrote:
I like arrow operators :-) But I fear that it will be too easy to misread `=>` as greater than or equal to, especially when skimming code. Assuming that they need to be different arrows, how do you feel about `->` for types and annotations, and `-->` for lambdas? Or `->>`?

On 2021-10-02 08:59, Abdulla Al Kathiri wrote:
Let’s say I want to write a lambda function with no arguments that’s connected to a button in GUI coding, will blabla.connect(()=>print(“clicked”)) be used or will blabla.connect(=>print(“clicked”)) be used?
In the case of C#, the parentheses are optional if there's only one parameter, so: () => ... (x) => ... or x => ... (x, y) => ... (x, y, z) => ... etc.

Yeah empty parentheses for parameters-less function is the clear obvious way. Optional parenthesis for single parameter function is a wise choice. In fact, I read C# lambdas and they made really great design choices. I particularly like the statements lambda. How about doing it in Python with the set syntax and a little twist? The last item of the lambda set is the return value. Something like the following: (x, y) => {z = x + y, a = sqrt(z) - 10, a} The return value will be a. Basically if the last item is an expression, the return value will be the value of the expression. If the last item is an assignment statement, then the return value will be None. Only assignment statements and expressions are allowed. Other statements like for loop or with statement are too much anyways for lambda set. C# docs advise against using more than 3 statements in their statements lambda. Abdulla Sent from my iPhone

On Sun, Oct 3, 2021 at 9:04 AM Abdulla Al Kathiri <alkathiri.abdulla@gmail.com> wrote:
I don't like the ambiguity here - using a comma there is going to be annoying with tuple creation. And yes, that would be an extremely likely use-case, for example: people.sort(key=lambda p: (p.salary, p.name, p.id)) IMO assignment statements wouldn't be needed here. For things too complicated for assignment expressions, use def. ChrisA

Then use it with the normal expression lambda: people.sort(key=p => (p.salary, p.name, p.id)). You don’t need lambda set for that. If you want to use it, it will be like the following: people.sort(key=p => {(p.salary, p.name, p.id)}). The tuple expression is the last item of the set, so the tuple is the return value. Abdulla Sent from my iPhone
On 3 Oct 2021, at 2:25 AM, Chris Angelico <rosuav@gmail.com> wrote:
people.sort(key=lambda p: (p.salary, p.name, p.id))

Oh I forgot what if you want to return a set from your lambda? Maybe a lambda set should at least have one assignment statement to qualify it as one. Expressions only inside a set syntax will be just a normal set that doesn’t care about order as you pointed out. But a lambda set will care about the order just like when you do a normal multi-lines def function. def f(x): print(x) z = x + 3 return z Is equivalent to (x) => {print(x), z = x + 3, z} since “z = x + 3” is an assignment statement, this would be qualified as a lambda set. Normal set will not be possible as that will throw syntax error. So the side effect is a printed x and the return is just z. If you write something like the following: (x) => {print(x), x +3} Make x = 1 and the return value will be just a normal set of {None, 4} or {4, None} with a printed 1 as a side effect. Abdulla Sent from my iPhone

Chris Angelico wrote:
You are right, I didn't think of this ambiguity w.r.t. to the start of the expression. While `lambda` clearly marks where the lambda body starts, `?` doesn't do this. Actually the above example could also be translated to `lambda x: members.sort(key=(x[1], x[0]))` (which doesn't make sense, of course, but it's valid syntax).

I find this approach too cryptic compared to reading regular Python notation, my brain has to mode switch to make sense of it. Would a little extra ?: be too much add to make clear it's a lambda function, e.g. ?: ? > 0 instead of ? > 0 Also either approach *could *add multi-argument lambdas: ?1, ?2: ?1 > ?2 or just ?1 > ?2 I wonder though if there's a nice approach to have a no argument lambda? 🤔 Damian (he/him) On Wed, Sep 29, 2021 at 8:55 AM Dominik Vilsmeier <dominik.vilsmeier@gmx.de> wrote:

IIUC a lot of what is being discussed has been implemented by the "placeholder" package on PyPI Here: https://pypi.org/project/placeholder/ It allows using things like `min(data, key=_[-1])` or `_.age < 18` (just using language features instead of adding new syntax).

On Wed, Sep 29, 2021 at 09:11:35AM -0000, Dominik Vilsmeier wrote:
I think those are massively more cryptic and hard to read than an explicit lambda. Being too terse is worse that being too verbose: consider how few people use APL compared to how many use Java. "lambda" is admittedly an odd name for a concept, most familiar to computer science theorists, but at least it is pronouncable in English. We can read it and talk about it: "Pass a lambda as the key parameter." Whereas here how do we pronounce "?"? "Pass a question mark as the key parameter" sounds like *all you need do* is pass the literal ? symbol. In practice people are going to call it lambda, in which case you might as well just write lambda. And please take mercy on the poor beginners who have to Google for "Python ?" to try to work out what on earth this does. At least other punctuation marks are either well known from school maths or other languages.

Given that we have comprehensions that use s simple expression, and operator.itemgetter for common keys, the use cases for these simple lambdas are pretty rare these days. Sure, some folks seem to prefer map and filter as a matter of style, but I don’t think we need to create cryptic notation to make that easier, NOTE: I expect many of the examples you found in the stdlib predate comprehensions and itemgetter. -CHB On Wed, Sep 29, 2021 at 8:09 AM Steven D'Aprano <steve@pearwood.info> wrote:
-- Christopher Barker, PhD (Chris) Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython

On 2021-09-29 10:11, Dominik Vilsmeier wrote:
[snip] I'd prefer something like "x -> x" "x => x" as an equivalent to "lambda x: x": sorted(items, key=i -> i['key']) The advantage of using '->' is that it already exists. This has all been suggested before. There have also the suggestions of adding a None-coalesce operator '??', plus None-coalesce subscripting with '?[...]', etc, which would be Python's equivalent of the null-coalesce operator of some other languages.

On 2021-09-29 10:11, MRAB wrote:
I'd prefer something like "x -> x" "x => x" as an equivalent to "lambda x: x":
I continue to find all such syntaxes less readable even than lambda. The idea of using a hyphen and a greater-than sign to "draw" an arrow doesn't sit well with me. The only problem with lambda is that the word "lambda" (unlike other Python keywords) is unusual and its relation to its function obscure. If we're going to change something, we should just come up with a new keyword like "shortdef" or (perhaps with these new parser possibilities I've heard vague references to) just allow "def" to be used for lambdas somehow. To my eye, adding arrow-like syntax doesn't help anything. -- Brendan Barnwell "Do not follow where the path may lead. Go, instead, where there is no path, and leave a trail." --author unknown

Dominik Vilsmeier writes:
Hence the proposal is to add a new syntax via the new token `?`.
I find that unreadable. If you're going to add new syntax, either bite the bullet and just allow 'λ' for 'lambda' (my preference as an old lisper), or 'x → f(x)' (following MRAB's earlier suggestion). Or maybe omitting the function name from a def: 'def (x): f(x)'.

Over in typing-sig we're considering a new syntax for callable *types*, which would look like (int, int, str) -> float. A matching syntax for lambda would use a different arrow, e.g. (x, y, z) => x+y+z. On Wed, Sep 29, 2021 at 11:51 AM Stephen J. Turnbull < stephenjturnbull@gmail.com> wrote:
-- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

On Wed, Sep 29, 2021 at 02:09:03PM -0700, Guido van Rossum wrote:
I like arrow operators :-) But I fear that it will be too easy to misread `=>` as greater than or equal to, especially when skimming code. Assuming that they need to be different arrows, how do you feel about `->` for types and annotations, and `-->` for lambdas? Or `->>`?

On 2021-10-02 08:59, Abdulla Al Kathiri wrote:
Let’s say I want to write a lambda function with no arguments that’s connected to a button in GUI coding, will blabla.connect(()=>print(“clicked”)) be used or will blabla.connect(=>print(“clicked”)) be used?
In the case of C#, the parentheses are optional if there's only one parameter, so: () => ... (x) => ... or x => ... (x, y) => ... (x, y, z) => ... etc.

Yeah empty parentheses for parameters-less function is the clear obvious way. Optional parenthesis for single parameter function is a wise choice. In fact, I read C# lambdas and they made really great design choices. I particularly like the statements lambda. How about doing it in Python with the set syntax and a little twist? The last item of the lambda set is the return value. Something like the following: (x, y) => {z = x + y, a = sqrt(z) - 10, a} The return value will be a. Basically if the last item is an expression, the return value will be the value of the expression. If the last item is an assignment statement, then the return value will be None. Only assignment statements and expressions are allowed. Other statements like for loop or with statement are too much anyways for lambda set. C# docs advise against using more than 3 statements in their statements lambda. Abdulla Sent from my iPhone

On Sun, Oct 3, 2021 at 9:04 AM Abdulla Al Kathiri <alkathiri.abdulla@gmail.com> wrote:
I don't like the ambiguity here - using a comma there is going to be annoying with tuple creation. And yes, that would be an extremely likely use-case, for example: people.sort(key=lambda p: (p.salary, p.name, p.id)) IMO assignment statements wouldn't be needed here. For things too complicated for assignment expressions, use def. ChrisA

Then use it with the normal expression lambda: people.sort(key=p => (p.salary, p.name, p.id)). You don’t need lambda set for that. If you want to use it, it will be like the following: people.sort(key=p => {(p.salary, p.name, p.id)}). The tuple expression is the last item of the set, so the tuple is the return value. Abdulla Sent from my iPhone
On 3 Oct 2021, at 2:25 AM, Chris Angelico <rosuav@gmail.com> wrote:
people.sort(key=lambda p: (p.salary, p.name, p.id))

Oh I forgot what if you want to return a set from your lambda? Maybe a lambda set should at least have one assignment statement to qualify it as one. Expressions only inside a set syntax will be just a normal set that doesn’t care about order as you pointed out. But a lambda set will care about the order just like when you do a normal multi-lines def function. def f(x): print(x) z = x + 3 return z Is equivalent to (x) => {print(x), z = x + 3, z} since “z = x + 3” is an assignment statement, this would be qualified as a lambda set. Normal set will not be possible as that will throw syntax error. So the side effect is a printed x and the return is just z. If you write something like the following: (x) => {print(x), x +3} Make x = 1 and the return value will be just a normal set of {None, 4} or {4, None} with a printed 1 as a side effect. Abdulla Sent from my iPhone
participants (13)
-
Abdulla Al Kathiri
-
Brendan Barnwell
-
Chris Angelico
-
Christopher Barker
-
Damian Shaw
-
Dennis Sweeney
-
Dominik Vilsmeier
-
Eric Fahlgren
-
Guido van Rossum
-
MRAB
-
Ricky Teachey
-
Stephen J. Turnbull
-
Steven D'Aprano