Simple multi-line lambda syntax

Supports type hints and does not break python semantics afaik

app.watch('message', lambda (message: str, origin: str) -> bool: (
    # multi-line code here
))

Isn’t this already possible?

>>> print(lambda msg: (
...     print(msg),
...     print(msg),
... ))
<function <lambda> at 0x7f4e5da1fd80>
>>> 

Multiple lines have never been disallowed. But lambda functions have always been defined as a single expression, as opposed to def functions which are a suite of statements. Your proposal would still have the code enclosed in parentheses and therefore almost certainly an expression; if it isn’t, that would be a far greater backward compatibility break than you were expecting.

1 Like

Already possible as long as you have no assignments within parentheses. i.e. print(msg) can not be replaced with msg = ....

from __future__ import braces

2 Likes

Even assignment expressions work with lambda expressions.

>>> (lambda x: (a:=x*2) + a)(3)
12

Name ‘a’ is not defined outside the expression, just as it would not be for the exquivalent def statement and call.

With assignment expressions and a function to select a return expression, you can get most of the way there.

returning = lambda *exprs: itemgetter(-1)(exprs)
noreturn = lambda *exprs: None

lambda arg1, arg2: noreturn(
    expr1,
    expr2,
    ...
)

app.watch('message', lambda message, origin: returning(
    # expression list
))

I do think the type annotations would be nice for lambdas. I think I’ve seen discussions where it was generally rejected, but I can’t readily find them now.

That’s true, although in this situation, I would much rather go the other direction, and convert the call into a decorator:

def watch(what):
    def inner(cb):
        app.watch(what, cb)
        return cb
    return inner

@watch("message")
def incoming_message(message, origin):
    ...
2 Likes

Huh, I don’t think I’ve seen that pattern of decorating callbacks before, but makes sense. I think I would generalize it to not include any interesting detail inside the decorator, so everything is right at function definition:

from functools import partial

def callback(caller, *args, **kwargs):
    init = partial(caller, *args, **kwargs)
    def inner(cb):
        init(cb)
        return cb
    return inner

...

@callback(app.watch, "message")
def incoming_message(message: str, origin: str) -> bool:
    ...