|
|
Subscribe / Log in / New account

Alternative syntax for Python's lambda

LWN.net needs you!

Without subscribers, LWN would simply not exist. Please consider signing up for a subscription and helping to keep LWN publishing

By Jake Edge
March 3, 2021

The Python lambda keyword, which can be used to create small, anonymous functions, comes from the world of functional programming, but is perhaps not the most beloved of Python features. In part, that may be because it is somewhat clunky to use, especially in comparison to the shorthand notation offered by other languages, such as JavaScript. That has led to some discussions on possible changes to lambda in Python mailing lists since mid-February.

Background

This is far from the first time lambda has featured in discussions in the Python community; the search for a more compact and, perhaps, more capable, version has generally been the focus of the previous discussions. Even the name "lambda" is somewhat obscure; it comes from the Greek letter "λ", by way of the lambda calculus formal system of mathematical logic. In Python, lambda expressions can be used anywhere a function object is needed; for example:

    >>> (lambda x: x * 7)(6)
    42

In that example, we define an anonymous function that "multiplies" its argument by seven, then call it with an argument of six. But, of course, Python has overloaded the multiplication operation, so:

    >>> (lambda x: x * 7)('ba')
    'bababababababa'

Meanwhile, lambda can be used in place of def, though it may be of dubious value to do so:

    >>> incfunc = lambda x: x + 1
    >>> incfunc(37)
    38

    # not much different from:
    >>> def incfunc(x):
    ...     return x + 1
    ... 
    >>> incfunc(37)
    38

Lambdas are restricted to a single Python expression; they cannot contain statements, nor can they have type annotations. Some of the value of the feature can be seen in combination with some of the other functional-flavored parts of Python. For example:

    >>> list(filter(lambda x: x % 2 == 1, range(17)))
    [1, 3, 5, 7, 9, 11, 13, 15]

There we use the filter() function to create an iterator, then use list() to produce a list of the first eight odd numbers, with a lambda providing the test function. Over time, though, other Python features supplanted these uses for lambda expressions; the same result as above can be accomplished using a list comprehension:

    >>> [ x for x in range(17) if x % 2 == 1 ]
    [1, 3, 5, 7, 9, 11, 13, 15]

The most obvious use of lambda may be as key parameters to list.sort(), sorted(), max()/min(), and the like. That parameter can be used to extract a particular attribute or piece of each object in order to sort based on that:

    >>> sorted([ ('a', 37), ('b', 23), ('c', 73) ], key=lambda x: x[1])
    [('b', 23), ('a', 37), ('c', 73)]

Arrow operators?

A thread about "mutable defaults" on the python-list mailing list made its way over to python-ideas when James Pic posted a suggestion for an alternate lambda syntax. His ultra-terse syntax did not strike a chord with the others participating in the thread, but it led "Random832" to suggest looking at the "->" or "=>" arrow operators used by other languages, such as C#, Java, and JavaScript.

It's worth noting that all three of these are later additions to their respective languages, and they all have earlier, more difficult, ways of writing nested functions within expressions. Their designers saw the benefit of an easy lambda syntax, why don't we?

Former Python benevolent dictator Guido van Rossum agreed: "I'd prefer the JavaScript solution, since -> already has a different meaning in Python return *type*. We could use -> to simplify typing.Callable, and => to simplify lambda." He also answered the question by suggesting that the "endless search for for multi-line lambdas" may have derailed any kind of lambda-simplification effort. Van Rossum declared multi-line lambda expressions "un-Pythonic" in a blog post back in 2006, but in the thread he said that it is not too late to add some kind of simplified lambda.

Steven D'Aprano was concerned about having two separate "arrow" operators. "That will lead to constant confusion for people who can't remember which arrow operator to use." He said that the "->" symbol that is already in use for the annotation of return types could also be used to define anonymous functions. It is also a well-known idiom:

There are plenty of popular and influential languages that use the single line arrow -> such as Maple, Haskell, Julia, CoffeeScript, Erlang and Groovy, to say nothing of numerous lesser known and obscure languages.

He also posted a lengthy analysis of how both uses of the single-line arrow could coexist, though it turned out that there is a parsing ambiguity if type annotations are allowed in "arrow functions" (i.e. those defined using the single-line arrow). One can perhaps be forgiven for thinking that the example he gives is not entirely Pythonic, however:

    (values:List[float], arg:int=0 -> Type) -> expression

[...] In case anyone is still having trouble parsing that:

    # lambda version
    lambda values, arg=0: expression

    # becomes arrow function
    (values, arg=0) -> expression

    # add parameter annotations
    (values:List[float], arg:int=0) -> expression

    # and the return type of the function body (expression)
    (values:List[float], arg:int=0 -> T) -> expression

The obscurity of the name "lambda" also came up. Brendan Barnwell lamented the choice of the name as confusing, while Ned Batchelder called it "opaque":

People who know the background of lambda can easily understand using a different word.  People who don't know the background are presented with a "magic word" with no meaning.  That's not good UI.

But, as D'Aprano pointed out, it is far from the only jargon that Python (and other language) programmers will need to pick up:

[It's] no more "magic" than tuple, deque, iterator, coroutine, ordinal, modulus, etc, not to mention those ordinary English words with specialised jargon meanings like float, tab, zip, thread, key, promise, trampoline, tree, hash etc.

While Paul Sokolovsky is a big fan of the lambda keyword, he does think that differentiating between the two uses of the arrow notation (the existing use for return types and a possible future use for defining short functions) is important. He thinks it would be better to use two different arrows; for defining functions, he is in favor of the double-line arrow (=>) instead.

Proof of concept

To demonstrate the idea, he came up with some proof-of-concept code that implements the double-line arrow as a lambda replacement. Here are some examples that he gave:

    >>> f = (a, b) => a + b    # not actual Python syntax
    >>> print(f(1, 2))
    3
    >>> print(list(map((x) => x * 2, [1, 2, 3, 4])))    # nor this
    [2, 4, 6, 8]

Cade Brown did not like the double-line arrow, on general principles, but Sokolovsky reiterated his belief that the two uses of arrows should be distinct; he also thinks that using the same symbol as JavaScript has some advantages. D'Aprano, however, is not convinced that following JavaScript's notation is necessarily the right thing for Python, nor does he think there is a need to separate the two uses of arrows. As might be guessed, others disagree; it was, to a certain extent, a bikeshedding opportunity, after all.

For his part, Van Rossum was not really opposed to using the single-line arrow for function definitions if there were no technical barriers to overlapping the two uses. No one seemed to think that allowing type annotations for lambda functions, which are generally quite self-contained, was truly needed. On the other hand, both David Mertz and Ricky Teachey were opposed to adding new syntax to handle lambdas, though Teachey thought it would make more sense if it could be used for both unnamed and named functions:

But if we could expand the proposal to allow both anonymous and named functions, that would seem like a fantastic idea to me.

Anonymous function syntax:

(x,y)->x+y
Named function syntax:
f(x,y)->x+y

That was something of a step too far for Van Rossum, though. There is already a perfectly good way to create named functions, he said:

Proposals like this always baffle me. Python already has both anonymous and named functions. They are spelled with 'lambda' and 'def', respectively. What good would it do us to create an alternate spelling for 'def'?

[...] I can sympathize with trying to get a replacement for lambda, because many other languages have jumped on the arrow bandwagon, and few Python first-time programmers have enough of a CS background to recognize the significance of the word lambda. But named functions? Why??

Greg Ewing hypothesized that it simply comes from the desire for brevity: "In the situations where it's appropriate to use a lambda, you want something very compact, and 'lambda' is a rather long and unwieldy thing to have stuck in there." D'Aprano added that it may come from mathematical notation for defining functions, which is not necessarily a good match with Python's def:

So there is definitely some aesthetic advantage to the arrow if you're used to maths notation, and if Python had it, I'd use it.

But it doesn't scale up to multi-statement functions, and doesn't bring any new functionality into the language, so I'm not convinced that its worth adding as a mere synonym for def or lambda or both.

While there was some support for the idea, and Sokolovsky is particularly enthusiastic about it, so far there have been no plans mentioned for a Python Enhancement Proposal (PEP). Adopting an arrow syntax for lambda expressions may just be one of those topics that pops up occasionally in Python circles; maybe, like the recurring request for a Python "switch", it will evolve into something that gets added to the language (as with pattern matching). On the other hand, lambda may be one of those corners of the language that is not used frequently enough to be worth changing. Only time will tell.


Index entries for this article
PythonEnhancements
PythonLambda


(Log in to post comments)

Alternative syntax for Python's lambda

Posted Mar 3, 2021 23:13 UTC (Wed) by NYKevin (subscriber, #129325) [Link]

As far as named lambdas go, you can already do this with the walrus operator:

>>> list(map(f := lambda x: x * 7, [1, 2, 3]))
[7, 14, 21]
>>> f(4)
28

Maybe arrows are better than the lambda keyword, but we've already got a perfectly good way to give names to arbitrary inner expressions. Why invent an alternative syntax just for lambdas? Given how many people vehemently hated the walrus before, during, and after the PEP process, I would naively expect this to go down in flames.

Alternative syntax for Python's lambda

Posted Mar 4, 2021 1:20 UTC (Thu) by rschroev (subscriber, #4164) [Link]

I hope this go down in flames.

This doesn't change any of the restrictions of lambda: still doesn't allow anything other than one single expression (which I don't think is a problem: if you need anything else, it's easy enough to create a named function). This proposal only changes the syntax a bit. Yet another infraction of the Zen of Python ("There should be one-- and preferably only one --obvious way to do it" in this case), for no real gain. And that change in syntax is contrary to Python's history of favoring clear words over a soup of symbols.

Alternative syntax for Python's lambda

Posted Mar 4, 2021 13:21 UTC (Thu) by t-v (guest, #112111) [Link]

Maybe it is the "heavy handed process with the approval by committee approach" for Python enhancements but the recent feature additions all look like bikesheds to me.

From := to switch to this, I really think that Python is losing slowly using the signature "anyone can read the code to understand what it does" property.

And the "but the beginners" argument looks precisely backward to me. When I am a beginner not knowing lambda and see

def fn(f : Callable) -> int:
...

fn(a -> 2 * a)

what will I ask? "What's with the ascii-art arrow? No the second one?" Ha!
With

def fn(f : Callable) -> int:
...

fn(lambda a: 2 * a)

I can ask "what's with the lambda"?

Using the same thing for two purposes is a bad idea and using line noise instead of words to structure programs does not appear to be making things more beginner-friendly to me. Having these things as words gives us words to talk about them and I don't have to know if it's an arrow or a pointer or a cursor. ("_" is one of these things that is ultimately hard to pronounce, too, and ":="). I think not having ";" much makes Python easier to learn not lest because people don't have to remember what a "semicolon" is.

Alternative syntax for Python's lambda

Posted Mar 5, 2021 12:22 UTC (Fri) by plugwash (subscriber, #29694) [Link]

> I can ask "what's with the lambda"?

and you can google "python lambda" and find a bunch of pages describing them.

On the other hand my experience is that search engines mostly ignore symbols, if I google "python {" I just get the python homepage, not a description of python sets and dictionaries.

Alternative syntax for Python's lambda

Posted Mar 6, 2021 7:04 UTC (Sat) by marcH (subscriber, #57642) [Link]

> Having these things as words gives us words to talk about them and I don't have to know if it's an arrow or a pointer or a cursor.

... and for that reason everyone and will keep calling the new arrow notation... "lambda"! So much for "beginner-friendliness".

The beginner-friendly name is "anonymous function" but good luck getting non-beginners used to something that long.

> > > [It's] no more "magic" than tuple, deque, iterator, coroutine, ordinal, modulus, etc, not to mention those ordinary English words with specialised jargon meanings like float, tab, zip, thread, key, promise, trampoline, tree, hash etc.

Nice one.

Alternative syntax for Python's lambda

Posted Mar 26, 2021 14:29 UTC (Fri) by quantzur (guest, #151322) [Link]

The lambda function still doesn't know it's own name:

>>> def g(x): return x * 7
...
>>> g(4)
28
>>> g.__name__
'g'
>>> list(map(f := lambda x: x * 7, [1, 2, 3]))
[7, 14, 21]
>>> f(4)
28
>>> f.__name__
'<lambda>'

Alternative syntax for Python's lambda

Posted Mar 4, 2021 5:41 UTC (Thu) by Otus (subscriber, #67685) [Link]

I'm almost starting to wish for a Python 4. The 2->3 transition was bad, but having a stable 2.7 for a decade was great.

Alternative syntax for Python's lambda

Posted Mar 4, 2021 23:01 UTC (Thu) by da4089 (subscriber, #1195) [Link]

I think the Python3 decade has actually ruined Python, not for its outcomes in the language (that topic is exhausted already), but for the culture it has bred in the core developers.

Python 2.7 reached its enormous popularity for being a great balance of complexity and ease of use, which led to PyPI, and it's current status of tool-of-choice for a massive number of programmers. But there were a couple of warts that just annoyed people enough that it was decided to break things, just this once, to fix them. In hindsight, that was probably a mistake, but the language had enough momentum to overcome it, and here we are at 3.10.

Unfortunately, that decade has bred a culture in the core team of believing it's ok to make major changes to the language. The half-baked async/await that dribbled in over the mid 3.x series, pattern matching, and now other frivolous thought bubbles are being taken seriously as potential additions to the language.

There is a fundamental tension to being a language designer/implementer: knowing when to stop. There's a point where you need to start working on a new language, rather than trying to shoehorn your latest novelty into the thing you've already built. It's tough to leave your massive success to one side, and start again at the bottom, but if you don't you end up creating C++, and not C.

I would love to have a Python 4.0 which is a feature freeze of Python 3.x, and the announcement of Snake-0.3b1: like Python but with X and Y and also Z!

(ps. yeah, yeah, get of my lawn, etc)

Alternative syntax for Python's lambda

Posted Mar 5, 2021 7:13 UTC (Fri) by dvdeug (subscriber, #10998) [Link]

There is a fundamental tension to being a language designer/implementer: knowing when to stop. There's a point where you need to start working on a new language, rather than trying to shoehorn your latest novelty into the thing you've already built. It's tough to leave your massive success to one side, and start again at the bottom, but if you don't you end up creating C++, and not C.

And C++ is one of the most successful languages of all time. Fortran 2018 is a horrible mess, but modern Fortrans are still used by people and groups that have been using Fortran for decades. Hyperextended Pascal variants are still used today, whereas Modula-2 and Oberon aren't. Python, Java, C++ and JavaScript are the biggest languages out there and they're extended forms of languages that are at least 25 years old. There's aesthetic reasons to start fresh, but if you want your programming language feature to be used, it's probably going to see much more use if you make it an extension for Python, JavaScript, Java, C++ or C#.

Alternative syntax for Python's lambda

Posted Mar 10, 2021 9:36 UTC (Wed) by anselm (subscriber, #2796) [Link]

And C++ is one of the most successful languages of all time.

C++: The COBOL of the 1980s.

Alternative syntax for Python's lambda

Posted Mar 6, 2021 15:23 UTC (Sat) by Polynka (guest, #129183) [Link]

> There's a point where you need to start working on a new language
> I would love to have a Python 4.0 which is a feature freeze of Python 3.x, and the announcement of Snake-0.3b1: like Python but with X and Y and also Z!

Erm…

*looks at ~Perl 6~ Raku*

I don’t think that it’s actually a great idea.

(and I mean its adoption, not the language itself)

Alternative syntax for Python's lambda

Posted Mar 15, 2021 7:37 UTC (Mon) by flussence (subscriber, #85566) [Link]

A delayed language will eventually be good; a language that gets rushed to meet nonsense deadlines will become the legacy code everyone curses for decades to come.

The most baffling thing to me about the whole epic of Raku isn't how it concluded, but that as recently as last week I still see people who think they're clever for regurgitating hateful Perl 6 FUD from the era when it was still called Perl 6, Slashdot was a relevant tech site, and comparisons to Duke Nukem Forever were still valid. The language is a good tool to write in, but it's accidentally just as valuable for its ability to quickly expose the sort of people with ossified brains and odious worldviews most of us wouldn't want anything to do with.

Alternative syntax for Python's lambda

Posted Mar 10, 2021 1:38 UTC (Wed) by milesrout (subscriber, #126894) [Link]

>Python 2.7 reached its enormous popularity for being a great balance of complexity and ease of use, which led to PyPI, and it's current status of tool-of-choice for a massive number of programmers. But there were a couple of warts that just annoyed people enough that it was decided to break things, just this once, to fix them.

This is revisionist nonsense. Python 3 development was started long before Python 2.7 existed. PyPI dates back to at least 2003, before Python 2.4 was released, let alone 2.7 (which wasn't released until 2010, seven years later). Meanwhile, Python 3 was first officially released in 2008, but had been floating around as an idea since at least 2006 if not earlier. PEP3000 dates to April 2006, and---as far as I know---was not the first suggestion of a backwards incompatible Python release.

Alternative syntax for Python's lambda

Posted Mar 5, 2021 22:13 UTC (Fri) by flussence (subscriber, #85566) [Link]

If Python starts a clean break and major refactoring to clean up 3.x's myriad gobbledygook syntax extensions right now, using all the resources and prior art available to them, then it'll be caught up to where Raku is today in significantly less time than the 20 years of effort that took. Maybe optimistically it'd be 5 years. *If*.

If it doesn't, well… people already complain that it's the new Fortran but at this rate it's going to be the new PHP too. Looking forward to that next “fractal of awfulness” blogpost…

Alternative syntax for Python's lambda

Posted Apr 15, 2021 20:20 UTC (Thu) by wtanksleyjr (subscriber, #74601) [Link]

Does anyone want to be where Raku is today? Or for that matter where Perl plus Raku is today? Perl has lost steam since 2005-ish, and Python only just stepped up circa 2018. Python didn't eat Perl's lunch; it looks like something that's internal to Perl itself.

Alternative syntax for Python's lambda

Posted Apr 16, 2021 11:02 UTC (Fri) by hummassa (guest, #307) [Link]

Raku is actually an useful language to learn and use today. Yes, it does not have the (deserved) place in the pantheon that Python has. It has to maturate its tools (yay more compiler optimizations) and its frameworks. I use it regularly, and with nice results on my field (data integration and analysis).

Alternative syntax for Python's lambda

Posted Apr 18, 2021 10:28 UTC (Sun) by flussence (subscriber, #85566) [Link]

>Does anyone want to be where Raku is today?

It's the easiest way today (and for the past 6 years or so) to run Python 2 and 3 code in an environment with modern quality-of-life features like arrow functions, switch statements and threads. I'm sure there's demand for that somewhere.

Alternative syntax for Python's lambda

Posted Mar 4, 2021 13:51 UTC (Thu) by andrewsh (subscriber, #71043) [Link]

How about making it possible to use def without a name?
a = def (x: int, y: bool): return x if z else 0

Alternative syntax for Python's lambda

Posted Mar 5, 2021 1:28 UTC (Fri) by NYKevin (subscriber, #129325) [Link]

Then you have one of three problems:

1. The anonymous def is an expression which contains one or more statements. Whitespace is normally not significant inside of an expression, but statements have an indentation level, so one of those two rules has to give. At a technical level, this is probably solvable (although the fact that you would be able to nest an anonymous def inside of another anonymous def makes it harder), but at the human level, it makes indentation significantly harder to reason about.
2. The anonymous def is a statement which contains one or more statements. Then you can't nest it inside of arbitrary expressions, and so it is a lot less flexible. You might as well just use a regular def.
3. The anonymous def is an expression which contains exactly one expression. Then it is no more than an alternative spelling of lambda. Why bother?

This has been discussed many times before, and I think it is unlikely that we'll see any sort of "happy middle ground" emerge any time soon.

Alternative syntax for Python's lambda

Posted Mar 10, 2021 2:43 UTC (Wed) by milesrout (subscriber, #126894) [Link]

The first solution is entirely doable. It's not hard to formally specify how indentation and whitespace inside indentation-insensitive expressions would work in a way that is quite intuitive, and where writing something that doesn't parse gives you an *error* instead of failing silently.

The way it works currently in Python (or at least you can conceptualise it this way to produce exactly the same result) is that the scanner/lexer parses each token, including whitespace tokens. Then it joins 'continuation lines' joined by backslashes, determines initial whitespace at the beginning of each line and then basically deletes the remaining whitespace, converting it into newline, indent and dedent tokens. Then any indent/dedent/newline tokens inside parens are deleted, so that this continues to work:

x = foo(1,
    2)
That's seen by the parser as
x = foo ( 1 , 2 ) NEWLINE.
not as
x = foo ( 1 , NEWLINE INDENT 2 ) NEWLINE DEDENT.
It would be quite simple: mark every newline and indent within a particular depth of bracketing with that depth, and then ignore any indents of greater depth than the current level of bracketing while parsing, and insert virtual dedent tokens when going down levels of nested bracketing.

so that would be

x = foo ( 1 , NEWLINE1 INDENT1 2 DEDENT1 ) NEWLINE
so if you had in fact written
x = foo(if True:
          2
        else:
          3)
you'd get
x = foo ( if True : NL1 IN1 2 NL1 DE1 else : NL1 ID1 3 NL1 DE1 ) NL1
From the perspective of whatever is parsing at the top level, that's:
x = foo ( [arbitrary tokens] ) NL
and from the perspective of whatever is parsing inside the parens, that's:
if True : NL IN 2 NL DE else : NL ID 3 NL DE
which is exactly what you get if you parse that statement at a top level in Python currently (at least conceptually):
if True: # if True : NL
    2 # IN 2 NL
else: # DE else : NL
    3 # IN 3 NL
# DE
It's also really easy to implement. I have done so myself as a proof of concept, which I later turned into the parser for a scripting language that is basically Python-with-multi-line-lambdas. It's a few extra lines in the scanner and a few extra lines in the parser. It doesn't slow anything down from a computational complexity perspective. And as a bonus, it gives you a hard error rather than silently-do-the-wrong-thing if you get nested indentation wrong.

I know it's a really popular meme in the Python community that it's OMG2HARD4ME to do multi-line lambdas in a way that gives intuitive, hard-to-get-wrong do-what-I-mean results and is easy and efficient to implement, but as far as I can tell it's just not true.

It would be massively useful. It wouldn't just give multi line lambdas, it would give the ability to nest any expression inside any other expression. All the Python special cases, like generator expressions (just use (for x in y: f(x))), a if b else c (just use a proper if statement, although that would be forced to be on at least 2 lines), etc. could be deprecated and phased out eventually.

Of course it will never happen in Python because it's remarkable conservative about syntax that actually matters while wildly liberal with making relatively useless syntax changes like :=. [You could just write foo((x = 1)) with my suggestion, by the way, which is a far nicer way of doing it than :=...]

Alternative syntax for Python's lambda

Posted Mar 13, 2021 13:53 UTC (Sat) by HelloWorld (guest, #56129) [Link]

Or one can just use one of the myriads of languages that got this right a decade or two ago (or 7 in the case of Lisp).

Alternative syntax for Python's lambda

Posted Mar 4, 2021 15:07 UTC (Thu) by LtWorf (subscriber, #124958) [Link]

I think for the sake of brevity it should be

λ a,b: a+b

Only 1 character instead of 2.

Alternative syntax for Python's lambda

Posted Mar 4, 2021 17:03 UTC (Thu) by aeline (subscriber, #144768) [Link]

I must say Racket's use of the actual unicode lamba symbol is my favorite syntax for lambas

Alternative syntax for Python's lambda

Posted Mar 4, 2021 21:05 UTC (Thu) by hasmanean (guest, #145244) [Link]

Yes, I think it's charming how programmers restrict ourselves to using the character set available on typewriters, which themselves use characters developed for convenience when writing using quill pens on animal-skin parchment.

Alternative syntax for Python's lambda

Posted Mar 10, 2021 2:08 UTC (Wed) by milesrout (subscriber, #126894) [Link]

The font faces we use today are not very much like the characters developed for convenience when writing "using quill pens on animal-skin parchment". Letter forms change drastically with every new medium of writing. When symbols were chiseled into stones you get sharp and angular runes. When symbols are pressed into clay with a stylus you get wedge-shaped cuneiform. When symbols are painted onto paper with brushes you get the calligraphic Chinese script. When symbols are painstakingly carved into elaborate burial chambers you get the exquisite Egyptian hieroglyphs. When the same symbols are hastily jotted onto papyrus with a reed pen you get hieratic.

When symbols are carved into a wax tablet you get what became our UPPERCASE latin alphabet, generally big sharp uncomplicated lines. When the same symbols are drawn onto a parchment or papyrus with a pen, you get the old Roman cursive, which basically became our lowercase.

The letterforms in modern fonts aren't even that similar to the script/cursive forms people use in handwriting today (see 'a' or 'g').

I honestly believe that programmers would use more unicode symbols if the input methods were better. Is that actually a good thing? There are a lot of Unicode confusables after all...

Alternative syntax for Python's lambda

Posted Mar 5, 2021 19:39 UTC (Fri) by cpitrat (subscriber, #116459) [Link]

Stupid question: how do you type it? Do you configure your editor to have a special shortcut? Do you know the char code by heart?

Alternative syntax for Python's lambda

Posted Mar 5, 2021 21:35 UTC (Fri) by nybble41 (subscriber, #55106) [Link]

I type λ using the XCompose input method. That's with this entry in my custom .XCompose file[0]:

<Multi_key> <g> <r> <l> : "λ" U03BB # GREEK SMALL LETTER LAMBDA

Here are the equivalents for lowercase a-z: αβ_δεφγηιθκλμνοπχρστυ_ωξψζ ('c' and 'v' have no corresponding entries and so were replaced with '_').
And the equivalents for uppercase A-Z: ΑΒ_ΔΕΦΓΗΙΘΚΛΜΝΟΠΧΡΣΤΥ_ΩΞΨΖ.
Also many useful symbols: → ⇒ ↑ ↓ × ÷ « » ⋄ ⊛ Ⓧ ¾ ⅞ ☺ € ₡ ¢ ∀ ∃ ±

Unfortunately Windows doesn't have any built-in equivalent to XCompose. I have a few char codes memorized for that, but λ isn't one of them.

To make this work under Linux with X11 you need to create the file ~/.XCompose, set GTK_IM_MODULE=xim and QT_IM_MODULE=xim, and assign some key to the Multi_key function in your keyboard settings. I use the Menu key for Multi_key but you might prefer something else.

[0] https://jessemcdonald.info/gogs/nybble/compose-config/raw...

Alternative syntax for Python's lambda

Posted Mar 5, 2021 21:43 UTC (Fri) by mpr22 (subscriber, #60784) [Link]

I use LeftWin for Compose myself. It's a lot easier to reach fluidly than Menu on a standard-pitch 104/105 key keyboard.

Alternative syntax for Python's lambda

Posted Mar 5, 2021 23:58 UTC (Fri) by mbunkus (subscriber, #87248) [Link]

While not built-in, I've been using the nice little Open Source program WinCompose[1] for quite a while now on Windows. It's easily configurable, both regarding the sequences as well as the key to use as the compose key, and it comes pre-configured with a wide range of sequences, a lot of which match the traditional XCompose sequences.

[1] http://wincompose.info

Alternative syntax for Python's lambda

Posted Mar 6, 2021 9:55 UTC (Sat) by LtWorf (subscriber, #124958) [Link]

On KDE, systemsettings allows you to pick a key to use as XComposeKey.

I don't think the env vars you are exporting are needed. I do not have them.

It used to be that GTK did not support longer sequences than 2 so I had to write them in kwrite and copy paste, but it seems to be working now for a while.

Alternative syntax for Python's lambda

Posted Mar 8, 2021 23:17 UTC (Mon) by nybble41 (subscriber, #55106) [Link]

> On KDE, systemsettings allows you to pick a key to use as XComposeKey.

Yes, that's how I have it configured. In other desktop environments you can use "setxkbmap -option compose:menu" (plus your normal model/layout options) for the same effect.

> I don't think the env vars you are exporting are needed.

If you don't set them then both Gtk and Qt will pick a default input method. If that happens to be XIM then everything will work just fine. If not, the .XCompose file might be ignored; it depends on which input method was chosen. IBUS has some support for reading .XCompose in recent versions. I'm not sure about the others.

Alternative syntax for Python's lambda

Posted Mar 6, 2021 1:49 UTC (Sat) by aeline (subscriber, #144768) [Link]

In emacs and DrRacket that hotkey Cmd-\

Alternative syntax for Python's lambda

Posted Mar 6, 2021 1:54 UTC (Sat) by aeline (subscriber, #144768) [Link]

DrRacket also allows most unicode, by typing \latexcode and pressing Cmd-Enter.
This is really nice for following mathematics/language papers. A snippet from a recent project:
;; Values
(v ::= n
b
∅ ;; Unit
(λ (x : t) e) ;; Value Abstraction
(Λ x e)) ;; Type Abstraction

Alternative syntax for Python's lambda

Posted Mar 10, 2021 3:50 UTC (Wed) by pj (subscriber, #4506) [Link]

I use kitty as a terminal and have a hotkey for unicode input where I can then type in the long name and choose it from a list. This brings up... which lambda?

U+39b is greek capital lambda
U+3bb is greek small letter lambda
U+1d27 is greek letter small capital lambda

...or one of the other 10ish mathematical lambda characters? (list at https://unicode-table.com/en/search/?q=lambda )

Is there a convention for this? is one more canonical than the others?

Alternative syntax for Python's lambda

Posted Mar 10, 2021 9:12 UTC (Wed) by mpr22 (subscriber, #60784) [Link]

.... personally, I think the answer should be "the ugaritic cuneiform one" :D

Alternative syntax for Python's lambda

Posted Mar 10, 2021 11:28 UTC (Wed) by fredrik (subscriber, #232) [Link]

In Gnome, and terminals like Alacritty, you use the generic unicode character shortcut, which is Ctrl + Shift + u followed by the unicode code point for lambda which is 03BB, which can be abreviated to 3bb, so basically:
Ctrl + Shift + u 3 b b Space
Easy as pie! 🙂

Alternative syntax for Python's lambda

Posted Mar 22, 2021 16:11 UTC (Mon) by hummassa (guest, #307) [Link]

<Ctrl+k>*l

:help digraphs

Alternative syntax for Python's lambda

Posted Mar 4, 2021 20:05 UTC (Thu) by eru (subscriber, #2753) [Link]

The lambda keyword has had this kind of meaning since the original LISP in the fifties. I find it kind of user-friendly compared to these terse notations. A thicket of punctuation signs is not readable.

Alternative syntax for Python's lambda

Posted Mar 5, 2021 11:53 UTC (Fri) by kpfleming (subscriber, #23250) [Link]

Indeed.
int& (*fpi)(int*) = [](auto* a)->auto& { return *a; };
Totally readable, only 50% punctuation.

Alternative syntax for Python's lambda

Posted Mar 5, 2021 18:08 UTC (Fri) by eru (subscriber, #2753) [Link]

Not to mention that thanks to the lack of automatic memory management in C++, using lambda there is a great way to introduce subtle bugs.

Alternative syntax for Python's lambda

Posted Mar 22, 2021 16:52 UTC (Mon) by hummassa (guest, #307) [Link]

Whoah? There is nothing but automatic memory management in C++, unless you are using new and delete, in which case I have a stack of books for you to read before you start over.

Alternative syntax for Python's lambda

Posted Mar 22, 2021 21:31 UTC (Mon) by zlynx (guest, #2285) [Link]

He probably means how you can define a C++ automatic reference capture lambda, then when it has a lifetime longer than those references the world blows up.

Java's garbage collection has a similar problem here though. Instead of exploding, your "innocent and harmless" capture holds a 300 MB data structure live in RAM.

I've seen C++ lambda captures hold references after a function exits, after a thread exits and even after the program itself exits. Threads continue running even after static object destruction.

Alternative syntax for Python's lambda

Posted Mar 23, 2021 0:27 UTC (Tue) by kpfleming (subscriber, #23250) [Link]

Yes, this exactly. It's trivially easy to accidentally capture 'this' in a lambda without extending the lifetime of that object.

Alternative syntax for Python's lambda

Posted Mar 24, 2021 0:00 UTC (Wed) by nix (subscriber, #2304) [Link]

Actually, a reference or two might be helpful. I've not really looked at the changes in C++ and what they imply for typical style since about 2000, and the language has moved on. I ought to catch up, but the relevant content appears to be scattered across hundreds of webpages each of which assumes you have read all the others first.

If there really is a book somewhere discussing how C++ has changed and how typical style has changed, I'd be very interested, because modern C++ has at some point since C++11 dissolved for me from "I can kind of read this if I squint" to "this is a pile of angle brackets and I really have no idea why they are here rather than somewhere else". So I think I have some relearning to do -- but from where?

Alternative syntax for Python's lambda

Posted Mar 5, 2021 19:18 UTC (Fri) by amarao (guest, #87073) [Link]

Can't we use rust notation for closures? |x, y| x + y - 2?

Rust has second form for closures with {}, but we can ignore those.

Alternative syntax for Python's lambda

Posted Mar 6, 2021 7:03 UTC (Sat) by aeline (subscriber, #144768) [Link]

I agree I really like this syntax (although I think Ruby had it first)
It feels very visually distinct from tuples, which I think is plus.

Alternative syntax for Python's lambda

Posted Mar 9, 2021 17:07 UTC (Tue) by NYKevin (subscriber, #129325) [Link]

The problem with that is, if you accidentally have any value to its immediate left, then it already parses as a very different expression:

>>> import ast
>>> print(ast.dump(ast.parse('z |x, y| x + y - 2', mode='eval'), indent=4))
Expression(
    body=Tuple(
        elts=[
            BinOp(
                left=Name(id='z', ctx=Load()),
                op=BitOr(),
                right=Name(id='x', ctx=Load())),
            BinOp(
                left=Name(id='y', ctx=Load()),
                op=BitOr(),
                right=BinOp(
                    left=BinOp(
                        left=Name(id='x', ctx=Load()),
                        op=Add(),
                        right=Name(id='y', ctx=Load())),
                    op=Sub(),
                    right=Constant(value=2)))],
        ctx=Load()))

(The indent= argument to ast.dump() requires Python 3.9+ for pretty-printing.)

That's not necessarily a complete non-starter, seeing as the lambda syntax probably would not allow you to put a value there, but it would be awkward if a small typo (e.g. omitting a comma) would result in a parse like that. Unfortunately, you can't just say "Well, x and y are non-existent variables, so don't parse it like that." An unrecognized variable is currently interpreted as an uninitialized global; it's assumed that said global will exist by the time the function is actually called (or else it's a runtime error). Python needs to continue to support that use case, or else recursive function calls won't work (functions are "just" variables that point at function objects).

Alternative syntax for Python's lambda

Posted Mar 7, 2021 0:15 UTC (Sun) by marcH (subscriber, #57642) [Link]

Maturity: when you ran out of important things to do and finally have time to discuss pointless changes.

Alternative syntax for Python's lambda

Posted Mar 8, 2021 12:16 UTC (Mon) by msuchanek (guest, #120325) [Link]

Somewhat off-topic, but to me, the syntax of map and filter is a bigger issue than lambda. I'd prefer them to be methods rather than functions.

This is pretty hard for me to read:

>>> map(lambda x: x + 1, range(5))

The lambda blends into the arguments and the comma is weak as a visual separator.

This would much easier:

>>> range(5).map(lambda x: x + 1)

Could that be partly to blame for the complaints about the lambda syntax?

Alternative syntax for Python's lambda

Posted Mar 9, 2021 17:32 UTC (Tue) by NYKevin (subscriber, #129325) [Link]

How would you propose we rewrite the following?

map(lambda x, y: x + y, range(5), reversed(range(5)))

I'm not sure that range(5).map(lambda x, y: x + y, reversed(range(5))) is an improvement. And I'm definitely not writing zip(range(5), reversed(range(5)).starmap(lambda x, y: x + y), because that's just ridiculous.

Alternative syntax for Python's lambda

Posted Mar 11, 2021 8:35 UTC (Thu) by kleptog (subscriber, #1183) [Link]

Well, in Elixir (which I think lifted the syntax from Ruby) it would look like:

range(5) |> zip(reversed(range(5)) |> map(lambda x, y: x + y)

Underwater this is converted to:

map(zip(range(5), reversed(range(5)), lambda x, y: x + y)

There is something pleasing about having the operator between the operands, but I don't think Python should go this route. It requires your entire standard library to be designed to make this work well. It's most appropriate for functional languages.

Alternative syntax for Python's lambda

Posted Mar 10, 2021 1:44 UTC (Wed) by milesrout (subscriber, #126894) [Link]

>I'd prefer them to be methods rather than functions.

This is not and will never be a good idea. So-called 'UFCS' proposals have failed repeatedly in C++ for good reason, and in Python. Standalone functions are not methods and the method syntax is ugly and misleading. Prioritising one argument over others nearly always makes no sense, and even when it does it's rare that it would be the first argument anyway. Why would it be `foo.map(lambda: ...)` or `foo.map(lambda: ..., bar)` and not `(lambda: ...).map(foo)` or `(lambda: ...).map(foo, bar)`?

I think that it is obvious that we should stick to calling functions as `f(x, y, z)` as we have done in many programming languages for decades and in mathematics for hundreds of years. `x.f(y, z)` makes no sense. `a.plus(b)` is just hideously ugly compared to `plus(a, b)`, and it's totally backwards anyway. If you were going to change to anything it should be `a b plus`, as then at least things would be evaluated entirely left-to-right.

Alternative syntax for Python's lambda

Posted Mar 14, 2021 12:49 UTC (Sun) by cbensf (guest, #120326) [Link]

Both would be valid choices in a fresh language, but in Python a lot of other choices have fallen into place to work better with them as functions.

* map() and filter() should work on any iterable. In Ruby calling them as methods works by all iterables inheriting the Mixin `Enumerable` which keeps growing with time.
But in Python the iteratable/iterator protocols are frozen and extremely narrow. All you need is `__iter__` / `__next__`. This is a feature.
Consider `itertools` module — it extends the set of things you can do with iterators simply by exporting more functions, without adding methods to built-in types. Similarly, countless people have written functions processing iterators, sharing some on PyPI.
[To be fair, *in practice* the Ruby culture of monkey-patching causes surprisingly little problems! So it's more cultural choice than technical argument.]

* There is also a question of symmetry when consuming several iterables, e.g. `zip(foos, bars)` or even the little-known `map(operator.add, foos, bars)`. These would look less pretty as methods on the first sequence...

* There are also various builtins "reducing" an iterable like `sum()`, `all()`, `any()`, `max()` etc. that combine pleasantly with generator expressions. E.g. to test if `n` is composable prime: `any(n % d == 0 for d in range(2, n))`.
In ruby this would be `(2...n).any? { |d| n % d == 0 }` which is a method on Enumerable that takes a block; so in Python this pattern of functions taking an iterator compensates for lack of blocks in some cases...

* A deeper argument IMHO, is that Python has a long tradition of separating the protocol a class has to implement from the public interface one calls.
It does this by operators and global functions, which adds flexibility and helps evolve the language:
- `a < b` operator started out in python 2 calling `a.__cmp__(b)` with fallback to `b.__rcmp__(a)`; later PEP 0207 added `a.__lt__(b)` and `b.__ge__(a)`.
- `bool()` checks `.__nonzero__()_` but falls back to `.__len__()`.
- `iter()` supports objects without `.__iter__` if they implement "sequence" protocol of `.__getitem__` with increasing integers. This allowed the for loop, but also everything else that wants to iterate, to keep working with older classes predating the iterator protocol.
- Even the trivial `.next()` method was wrapped in a builtin function `next()`, which helped abstract the later renaming to `.__next__` (PEP 3114).

Alternative syntax for Python's lambda

Posted Mar 25, 2021 20:06 UTC (Thu) by strombrg (subscriber, #2178) [Link]

I'm opposed to terse-ifying lambda in Python.

Lambda is rarely useful in Python - you're almost always better off using a generator expression, a list comprehension, or something from the operator module.

And lambdas tend to give rise to the software development equivalent of a runon sentence.

Naming a function in those rare cases that you genuinely need something custom really isn't the end of the world, and is more readable anyway.

Alternative syntax for Python's lambda

Posted Mar 31, 2021 20:53 UTC (Wed) by mina86 (guest, #68442) [Link]

> but it led "Random832" to suggest looking at the "->" or "=>" arrow
> operators used by other languages, such as C#, Java, and JavaScript.

>> It's worth noting that all three of these are later additions to
>> their respective languages, and they all have earlier, more
>> difficult, ways of writing nested functions within
>> expressions. Their designers saw the benefit of an easy lambda
>> syntax, why don't we?

I don’t know about C# but before lambda functions were introduced in
Java, one had to write dozens of lines to get a simple anonymous ‘x+y’
function. This is nothing like Python which has a short lambda
expression.

And as for JavaScript, arrow functions have different semantics than
anonymous functions. The arrow function wasn’t introduced because
‘function’ is such a long word but because programmers couldn’t
comprehend how ‘this’ variable worked. Again, this does not reflect
situation in Python.

Neither comparison is apt.


Copyright © 2021, Eklektix, Inc.
This article may be redistributed under the terms of the Creative Commons CC BY-SA 4.0 license
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds