Mailing List Archive

Confusing error message: lambda walruses
Using assignment expressions in lambda functions sometimes works, but
sometimes doesn't.

Python 3.11.0a0 (heads/main:dc878240dc, Oct 3 2021, 10:28:40) [GCC
8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.

# Fine if it's a parameter to the lambda function
>>> def f():
... return lambda x: (x, x := 2, x)
...
>>> g = f()
>>> g(1)
(1, 2, 2)

# Plain ol' SyntaxError if unparenthesized
>>> def f(n):
... return lambda: n := 1
File "<stdin>", line 2
return lambda: n := 1
^^
SyntaxError: invalid syntax

# Fine if it's a parameter to the outer function
>>> def f(n):
... return lambda: (n := 1)
...
>>> g = f(3)
>>> g()
1

# But a SyntaxError if parenthesized like this??
>>> def f(n):
... return (lambda: n := 1)
File "<stdin>", line 2
return (lambda: n := 1)
^^^^^^^^^
SyntaxError: cannot use assignment expressions with lambda

# Oh, and it doesn't actually assign anything.
>>> def f(n):
... return (lambda: (n := 1)), (lambda: n)
...
>>> g, h = f(5)
>>> h()
5
>>> g()
1
>>> h()
5


What's going on here?

Disassembly of the last function shows that it's using STORE_FAST to
try to assign to n, so I *think* that it's actually like the first
example, where the lambda function is able to assign to its own
locals, whether they're parameters or not. But the two SyntaxErrors
are confusing.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: Confusing error message: lambda walruses [ In reply to ]
On 2021-10-03 10:39:38 +1100, Chris Angelico wrote:
> Using assignment expressions in lambda functions sometimes works, but
> sometimes doesn't.

> ... return lambda x: (x, x := 2, x)
syntax ok

> return lambda: n := 1
syntax error

> ... return lambda: (n := 1)
syntax ok

> return (lambda: n := 1)
syntax error

> ... return (lambda: (n := 1)), (lambda: n)
syntax ok, but doesn't assign

...
> What's going on here?

I think it's the parentheses:

The right side of the lambda is defined as an ?expression?, not an an
?assignment_expression?, and there is no direct derivation from one to
the other in the grammar. So you can't put an ?assignment_expression? there
directly. But if you put it in parentheses, it becomes a ?starred_item?
which is a ?starred_expression? which is then part of the ?parenth_form?
which is an ?enclosure? which is an ?atom? which is (through several
more steps) an ?expression? which can be used on the right side of a
lambda.

hp

--
_ | Peter J. Holzer | Story must make more sense than reality.
|_|_) | |
| | | hjp@hjp.at | -- Charles Stross, "Creative writing
__/ | http://www.hjp.at/ | challenge!"
Re: Confusing error message: lambda walruses [ In reply to ]
On 2021-10-03 10:39:38 +1100, Chris Angelico wrote:
> # Oh, and it doesn't actually assign anything.
> >>> def f(n):
> ... return (lambda: (n := 1)), (lambda: n)
> ...
> >>> g, h = f(5)
> >>> h()
> 5
> >>> g()
> 1
> >>> h()
> 5

I thought about that one a bit more and I think that the lambda is
creating a new scope, just like a regular function would, so it is
assigning to its own private ?n?, not the ?n? in ?f(n)?.

https://docs.python.org/3/reference/expressions.html#grammar-token-lambda-expr
doesn't mention scope explicitely, but

| The unnamed object behaves like a function object defined with:
|
| def <lambda>(parameters):
| return expression

implies to me that it should create a new scope because a nested function
defined with ?def? would do that, too.

hp

--
_ | Peter J. Holzer | Story must make more sense than reality.
|_|_) | |
| | | hjp@hjp.at | -- Charles Stross, "Creative writing
__/ | http://www.hjp.at/ | challenge!"
Re: Confusing error message: lambda walruses [ In reply to ]
On 03/10/2021 01:39, Chris Angelico wrote:
> Using assignment expressions in lambda functions sometimes works, but
> sometimes doesn't.

Does this commit by a certain Chris Angelico help clear things up?

https://github.com/python/peps/commit/f906b988b20c9a8e7e13a2262f5381bd2b1399e2
--
https://mail.python.org/mailman/listinfo/python-list
Re: Confusing error message: lambda walruses [ In reply to ]
On Thu, Oct 7, 2021 at 8:51 AM Thomas Jollans <tjol@tjol.eu> wrote:
>
> On 03/10/2021 01:39, Chris Angelico wrote:
> > Using assignment expressions in lambda functions sometimes works, but
> > sometimes doesn't.
>
> Does this commit by a certain Chris Angelico help clear things up?
>
> https://github.com/python/peps/commit/f906b988b20c9a8e7e13a2262f5381bd2b1399e2

No, because the examples I gave don't fit into that :) I was aware of
what the PEP originally said.

If you try to align the examples with the descriptions in the PEP,
you'll find that they don't all match.

ChrisA
--
https://mail.python.org/mailman/listinfo/python-list
Re: Confusing error message: lambda walruses [ In reply to ]
On 06/10/2021 23:53, Chris Angelico wrote:
> On Thu, Oct 7, 2021 at 8:51 AM Thomas Jollans<tjol@tjol.eu> wrote:
>> On 03/10/2021 01:39, Chris Angelico wrote:
>>> Using assignment expressions in lambda functions sometimes works, but
>>> sometimes doesn't.
>> Does this commit by a certain Chris Angelico help clear things up?
>>
>> https://github.com/python/peps/commit/f906b988b20c9a8e7e13a2262f5381bd2b1399e2
> No, because the examples I gave don't fit into that :) I was aware of
> what the PEP originally said.
>
> If you try to align the examples with the descriptions in the PEP,
> you'll find that they don't all match.
>
> ChrisA


The issue closed by that commit is interesting, if nothing else:

Guido van Rossum wrote:

> The PEP says very little about lambda. I feel the following two
> examples should both be valid:
>
> foo(f := lambda: 42, flag=True)
> foo(lambda: f := 42, flag=True)
Chris Angelico wrote:

> The second example is kinda bizarre though; it's going to create a
> fairly useless name binding within the lambda function. (Unless you
> want to give lambda functions the same magic as comprehensions, making
> the name f exist in the same scope where foo is being run.) So I would
> be okay with the first example doing the obvious thing, and the second
> one requiring parentheses lambda: (f := 42) for syntactic validity.

I think that at least clears up this part:

> # But a SyntaxError if parenthesized like this??
>>>> def f(n):
> ... return (lambda: n := 1)
> File "<stdin>", line 2
> return (lambda: n := 1)
> ^^^^^^^^^
> SyntaxError: cannot use assignment expressions with lambda
>
> # Oh, and it doesn't actually assign anything.
>>>> def f(n):
> ... return (lambda: (n := 1)), (lambda: n)
> ...


--
https://mail.python.org/mailman/listinfo/python-list