
Stop writing lambda expressions in Python - Quyzyx
http://treyhunner.com/2018/09/stop-writing-lambda-expressions/
======
davemp
> I’d say that using lambda expressions is acceptable only if your situation
> meets all four of these criteria:

> 1\. The operation you’re doing is trivial: the function doesn’t deserve a
> name

Sure.

> 2\. Having a lambda expression makes your code more understandable than the
> function names you can think of

This is just vague. Locally defining a function right where you need it makes
code much more understandable to me more often than the wacky names people
often come up with. Naming things is hard!

> 3\. You’re pretty sure there’s not already a function that does what you’re
> looking for

So the reader of the code has to know what that built-in function does. How is
that much different than expecting the reader to know what a lambda is?

> 4\. Everyone on your team understands lambda expressions and you’ve all
> agreed to use them

Or just use them and have the members of your team better themselves by
learning a useful language feature.

\---

In my experience, names lie--functions don't. If a developer isn't going to
spend the time to write a nice comment, they aren't going to use a
comprehensible naming convention. In trivial cases where naming is easy, the
lambda is also usually very easy to parse.

~~~
siddboots
I've noticed a pattern in among python developers where the faux pas of
assigning a lambda is used to signify "I'm going to use this function in a
vaguely functional programming manner". For example, by passing it into map,
or filter, or sorted.

For this reason, I don't really mind it. It's a sort of cue to the reader
about the style of programming that will follow.

~~~
BerislavLopac
I many of those cases people use lambda when a partial would be a better fit.

------
andrewstuart
Python really needs much better anonymous functions - Lambda expressions just
don't cut it.

To be clear, what Python does need is multiline, inline anonymous functions.
In JavaScript/ES2015, the fat arrow function syntax is just remarkably
powerful in its ability to simplify code. I would go so far as to say that the
ES2015 fat arrow syntax completely changed the way my programs are written,
increasing power and reducing complexity. ES2015 fat arrows I think are
possible the best programming language feature I know.

async programming in JavaScript benefits massively from the beautifully terse
and powerful anonymous functions in ES2015. Python now has powerful async
programming, but no equivalent to the ES2015 terse inline function anonymous
function syntax.

I raised this issue elsewhere once and someone said "there will never be
multiline, inline anonymous functions in Python because it would require
brackets". If so then it is a great pity that Python is simply not capable of
including a language feature that is IMO absolutely critical.

Also, as mentioned elsewhere in this thread, using the word "lambda" to
describe anonymous functions is a really really bad decision. Lambda sounds
very deep, very computer sciencey and very complex. Probably they should just
be called anonymous functions in keeping with the rest of the industry, which
also is not a great name but is much better than "Lambda" functions.

Beginners might think along these lines: "Lambda .... lambda calculus? I don't
know calculus. I'm outta here."

Names matter.

~~~
informatimago
Python is fundamentaly flawed, since it's based on lines (and significant
indentation), and foremost, since it's based on statements, and not only on
expressions.

Lambda expressions look in python like a sore spot, because Python stresses so
much statements, and procedural programming.

IIRC, recently we've seen passing a paper in hacker news, that let beotians
write programs, and only ~10% IIRC (or less) of their uterances were
procedural statements!

~~~
harimau777
I've seen people make the distinction between statements and expressions in
the past, but I don't get the significance. I mean I understand what they are
but I don't understand why people view the concept as such a big deal.

Are there any resources explaining this which you would recommend?

~~~
wwright
Expressions can be nested arbitrarily and infinitely — like in math. You can
take “x * x” and “3 * x” and add them into “x _x + 3_ x”. You can pass that to
a function like “log(x _x + 3_ x)”. You could then divide that, square root
it, on and on. You can combine the expressions however you want.

Statements can also be combined, but in a separate way. You have control flow
blocks, and inside of those you have one serial list of statements. Control
flow blocks and statements can only go in other control flow blocks, and not
in expressions.

If you’re coming from languages like C or Python, that might not seem like a
huge deal. Why would you want “x = 5” or “while y < 10:” in an expression?

Languages that focus more on expressions have surprisingly simple ways of
expressing some things, though.

In Rust, “if” statements are fully expressions, so you can assign them:

    
    
        let x = if y > 10 {
            let a = readLine().parse();
            let b = readLine().parse();
            a + b
        } else {
            y * 2
        }
    

Another neat feature is that macros can return blocks, and you can still call
those macros wherever you like. For example, in the standard library, there is
a “try!” macro that checks if a value is successful. It expands basically
into:

    
    
        if result is successful {
            result.value
        } else {
            return result.error
        }
    

The “return” there basically causes the error to short-circuit up the stack.
In practice, that means that you can use “try!” to simply say “give me the
successful value, and if there was an error, just send it up the stack.”

If you wanna get really fancy, languages like Haskell and Elm don’t have side
effects, so things like “x = 5” don’t exist. There are only expressions. You
can create values that represent doing something (kind of like Redux actions),
and there are lots of tools for combining those action values. Because it’s
all expressions, you can combine them arbitrarily. If you have a common case
for how you combine your effects, you can just make a plain old function for
it, because it’s all just expressions.

------
nikofeyn
all i could think of is "stop writing python". it seems to me that a lot of
these problems are fundamental problems with python. i continue to not
understand why anybody likes python the language.

the fact that creating a function with lambda versus the normal way is
different is bonkers. for example, in f# (and other sane languages), the
following are identical:

    
    
      let test1 = fun x -> x * x
    
      let test2 x = x * x
    

both return:

    
    
      x:int -> int
    

are they really different in python or is it just a weird naming problem? the
article isn't explicit on the actual differences other than what is reported
by the REPL.

also, you can't pass operators as functions in python? in f#, you simply wrap
them in parentheses.

    
    
      let test3 f x y = f x y
    
      > test3 (*) 2 3 ;;
    
      val it : int = 6
    

i read an interview with hal abelson where he called python's lambda broken.
seems so.

edit: also, this guy's thoughts on map and filter, in my opinion, show the
python community's backwoods thinking regarding functional programming. yes,
it seems generator expressions are nice (they are called sequence expressions
in f#), but map and filter, even for his simple examples are much more clear.
they communicate better what is actually happening. the fact that he never
uses map and filter at all and then goes on to basically say map and filter
aren't even needed in python says a lot.

~~~
JasonFruit
Nobody really likes Python the language especially much, but lots of people
love Python the ecosystem, because it lets them get stuff done. I can think of
a dozen languages I like better than Python, but none that's more practical in
as many different contexts.

\--------------------

Edit: Apparently, there are some people who do like Python the language, and
some people who dislike Python the ecosystem. If you want to find a thing,
just say it doesn't exist, and it will find you.

~~~
alexchamberlain
I disagree... I came for the syntax and readability; I've stayed for the
community.

~~~
JasonFruit
I suppose I like the readability too, but there are a number of things about
the language I would rather had been done differently; I still insist that its
approach to scope is worse than any choice except making everything global.

~~~
AlexCoventry
If the local/ global scooping rules are hurting you that much, you are
probably putting more state in module-level variables than is healthy.

~~~
JasonFruit
It's not that: it's the hobbled function scope that doesn't handle nested
functions the way I would expect and hope.

------
whalesalad
Personally, I use them often and disagree with a lot of this sentiment. I also
disagree with a significant portion of PEP8, however.

Anonymous functions are vital to any modern high level language.

I think most people get hung up on the word. If the keyword was changed from
“lambda” to “fun” for example I think it wouldn’t be as obtuse to more
intermediate developers.

To be honest I think the lambda does Python a service vs a disservice. It
helps to illustrate that everything in Python is an object. Once you realize
you can treat functions similarly to how you treat things like integers and
strings your mind opens up to functional style. Assigning a function to a
variable the same way you do with a string, for instance, helps to reinforce
this concept.

~~~
cirgue
> Anonymous functions are vital to any modern high level language.

I don’t think you’re wrong, but I am also not experienced enough to know why
this would be the case. What is the virtue of having an anonymous function? I
get that it makes code somewhat easier to read and write if you’re only using
the function once and it’s short, but that seems like an edge case. What am I
missing?

~~~
jcrben
First class functions are vital. Not so sure that anonymity is vital.

------
carapace
I'm sorry but this article is mostly not Scottish. Lambdas in Python are an
_old_ non-issue. They are what they are.

I like 'em because you can do "stupid python tricks" like Church numerals:
[https://en.wikipedia.org/wiki/Church_encoding](https://en.wikipedia.org/wiki/Church_encoding)

    
    
        plus = lambda m: lambda n : lambda f: lambda x: m(f)(n(f)(x))
        succ = lambda n: lambda f: lambda x: f(n(f)(x))
        mult = lambda m: lambda n: lambda f: m(n(f))
        exp = lambda m: lambda n: n(m)
    
        c0 = lambda f: lambda x: x
        c1 = lambda f: lambda x: f(x)
        c2 = lambda f: lambda x: f(f(x))
        c3 = lambda f: lambda x: f(f(f(x)))
        c4 = plus(c1)(c3)
        c5 = plus(c2)(c3)
        c6 = plus(c1)(c5)
        c7 = succ(c6)
        c8 = succ(c7)
        c9 = c3(succ)(c6)
        c10 = mult(c2)(c5)
        c11 = succ(c10)
        c12 = mult(c3)(c4)
        c64 = c3(c4)
        c256 = exp(c2)(c8)

~~~
jwilk
What do you mean by "not Scottish"?

~~~
carapace
It's a Mike Myers bit from SNL. He plays a Scotsman who insists, "If it's not
Scottish it's crap!"

I mean it as a self-deprecating way to decry the quality of the post without
being too harsh. The author of the blog post had good intentions, but in my
opinion he should have put it off for another decade or so, to gain
perspective.

------
zmmmmm
The crippled nature of Python's lambdas are a big weakness of Python. They sit
in a no-mans land of providing half a solution but being too crippled to
actually to clarity / improve programming style.

I much prefer languages that go all the way and give you full integration like
Ruby, ES6 and Groovy. For example, in Groovy closures just blend completely
into the language:

    
    
        [1,2,3,4,5].grep { it > 3 }.collect { it * 5 }
    

Compare to python is almost incomprehensible:

    
    
        map(lambda y: y * 5, filter(lambda x: x > 3, [1,2,3,4,5]))
    

But breaking this into functions reduces to an almost silly level of
verbosity. Of course, idiomatic python would write this as a list
comprehension, but that is just proof that the lambda construct is broken, and
the list comprehension is a band-aid and doesn't scale up to more complex
scenarios.

~~~
vorg
Your Apache Groovy example doesn't work in Jenkins Pipelines, which cripples
Groovy so the functional methods such as `collect` don't work.

You wrote elsewhere that

> I won't miss Grails because I think that, a bit like Gradle, it is an
> antipattern use of Groovy - needlessly applying its dynamic features where
> they are not even required

Groovy was designed as a dynamic language to complement Java. Its static
compilation was tacked on much later, whereas Kotlin and Scala, like Java,
were designed to be statically typed from the ground up, and they, unlike
Groovy, also run on other platforms besides the JVM. If you're not going to
use Groovy's dynamic features, you might as well use Java, Kotlin, or Scala.

------
natch
Great post, love all the examples categorizing types of misuse (at least what
he deems as misuse, and I won't argue because I have a lot to learn by reading
through this).

Reminds me of a story.

A few years ago Peter Norvig (Google) posted a "Show HN" posts with one of his
amazing notebook excursions where he went through nice little problem,
building up a library of functions to make a solution. I think this has
happened multiple times.

One such time, one bit of code used a lambda expression, and I was new to
Python, and wondered in a possibly-slightly-whiney comment (as I recall)
whether it was the most readable way to do things.

Within an hour or so, his post was silently updated and the lambda keyword was
gone. I don't remember the nature of the rest of the change, whether it was
just the one block of code or more, but the code was more readable to me, at
least as a newbie, at the time. Not sure my comment had anything to do with
it, but I was impressed that he did make the change in any case. I have never
worked with him, but my respect for him grew with that tiny little incident.

BTW in case anyone feels the urge to explain lambda expressions to me now,
that's OK, no need, but thanks anyway.

------
michelpp
The argument used by the author for 'abs' and 'min' can also be useful for all
of the functions in the operator module:

[https://docs.python.org/3/library/operator.html](https://docs.python.org/3/library/operator.html)

In particular 'attrgettr' and 'itemgettr' are very useful functions to map
over objects and sequences.

~~~
Too
Is attrgetter recognized by static code checkers like mypy and pycharm? One of
the benefits of lambdas is that arguments autocomplete even without manual
type annotations.

------
wrs

        def length_and_alphabetical(string):
            """Return sort key: length first, then case-normalized string."""
            return (len(string), string.casefold())
    

You just said the same thing three different ways. Not having to do that is
exactly what lambda expressions are for.

------
sephware
The first two situations where he recommends avoiding lambda I would agree
with. But when he assigns `colors_by_length`, that's a perfectly valid usage
of lambda. It's actually an ideal example of why it exists. His
`length_and_alphabetical` is not an improvement. The tuple unpacking is
something that destructuring in lambda syntax would improve, as it did with
JavaScript. In ES6 it would be:

    
    
        const points = [[[1, 2], 'red'], [[3, 4], 'green']]
        const points_by_color = points
          .sort(([point, color]) => color)
    

I know it's kind of futile to argue against a point using what-ifs, but
personally I wish Python embraced this style of functional programming a
little bit _more_ and adapted its syntax to it, rather than coming across
articles where people recommend against this style just because the language
isn't (currently) as suitable to it.

~~~
rcfox
Python 2.7 had tuple parameter unpacking, but it was removed in Python 3.

[https://www.python.org/dev/peps/pep-3113/](https://www.python.org/dev/peps/pep-3113/)

------
manicdee
Headline should actually be: “Stop abusing lambdas in Python: how you are
doing it wrong and how you can do it better.”

This tutorial is not an argument against lambdas, but an argument against
abusing Python lambdas.

------
swalladge
The author uses this as an argument against lambdas:

> lambda expressions are an odd and unfamiliar syntax to many Python
> programmers

Well these python programmers now have an opportunity to learn this unfamiliar
syntax. You don't avoid using parts of a programming language just because
it's unfamiliar! To new Python programmers, a lot of the syntax is unfamiliar
at first, especially if coming from a curly-brace language or new to
programming in general.

While I like many of the ideas in the article (especially the use of standard
library functions that already do what your lambda was trying to do), it seems
to make some cases against using lambda where the justification isn't entirely
valid. :/

------
alexchamberlain
I can't agree with this article, but I fear it may be a case of over
simplified examples. For example, I generally prefer a simple lambda as a
`key` to a named function I have to go find the definition of when I want to
understand it and I find lambdas more readable than itemgetter at al. Ofc,
pass around normal functions and use generator expressions when you can.

------
yodsanklai
I don't see how anyone could have a hard time using lambdas... they've been
around forever, and most programming languages nowadays have them.

A good rule of thumb is given by the google python coding guidelines [1]:

"Okay to use them for one-liners. If the code inside the lambda function is
any longer than 60-80 chars, it's probably better to define it as a regular
(nested) function."

[1] [https://github.com/google/styleguide/blob/gh-
pages/pyguide.m...](https://github.com/google/styleguide/blob/gh-
pages/pyguide.md)

------
luord
Another post about discussing a functional programming tool in [insert
language with a different primary paradigm here], another few threads from the
functional programming fans stating how their _preference_ is absolutely
correct and how that paradigm is veritably better than any other.

It isn't, it's just a different way to approach programming.

As for the article, it isn't really a case against lambdas themselves, just
against misusing them. It certainly didn't make a strong enough case for me to
stop using them.

------
dancek
So in Python, imperative style is often more idiomatic than code that uses
lambda expressions.

While in some functional languages (Haskell) and array programming languages
(J), tacit or point-free style is often more idiomatic than lambda expressions
(or equivalent).

There's something funny about that.

~~~
dan-robertson
I somewhat agree but I think there are two things wrong with tacit style in
Haskell:

1\. It is usually taken too far (indeed I think flip is probably too far and
(.).(.) is certainly too far. I’m also not a big fan of eg fmap.fmap)

2\. (.) is the wrong way round. Code reads much better if it has type (a -> b)
-> (b -> c) -> (a -> c)

In APL or J was I don’t have enough experience to say other than that I find
it difficult to read but the puzzle is amusing

~~~
dancek
That comes down to what code is easiest to read, and that is different for
different people. If playing with the APL family has taught me something, it's
that what someone sees as a completely incomprehensible mess might be very
readable and intuitive to another.

I personally have a hard time understanding almost any point-free Haskell code
and deciphering a line of J might take me an hour. But as long as it's
readable to the intended audience it's alright. Of course it isn't sometimes.

------
jcelerier
> When I encounter a lambda expression in the wild, I often find that removing
> it improves code readability.

I have never seen a code that wasn't made clearer when moving from a three-
liner non-lambda somewhere upper in the code, to a one-liner lambda used at
the right place

~~~
weliketocode
I wouldn't say never, but I'd say the vast majority of the time this has been
the case for me as well.

------
tincholio
The whole article, and his reasoning, reminded me of PG's Blub programmer.
Seems like a perfect example. (that being said, lambdas in Python are rather
lame, though not for the reasons he wrote about)

------
ggregoire
> I get a lot of questions about lambda, I’m hesitant to recommend my students
> embrace Python’s lambda expressions.

Lambdas are in so many languages than it seems worth it to spend 10 minutes
learning what they are.

------
sixbrx
I generally like coding with lambdas, but in Python one thing I hate is that
they will capture a numeric loop index by ref and not by its current value. If
you try generating some polynomials x -> x, x^2, x^3 ... in the "obvious" way
you'll see what I mean, all polynomials will be the same as the last one after
the loop or list compr is done.

Theres a workaround using default arg value to capture by val but its ugly and
easy to forget and hard to "see" in review that its missing.

This is as much an issue of loops not generating "fresh" instances of their
index vars as it is a problem with lambdas, to be fair, but it still sucks,
esp in mathematical domains. Julia used to suffer from this but fixed it, I
believe the same is true for C# too.

~~~
istjohn
Do you mind fleshing out your example with some actual code? I'm not quite
following, and I'm completely lost by the time you talk about the workaround.

~~~
sixbrx
Sure, what I meant by the "obvious" way of making some polynomials is:

    
    
        polys = []
        for i in range(0,4):
            polys.append(lambda x: x**i)
    

But that actually just yields the same polynomial x -> x^3 in each element of
polys array:

    
    
        polys[0](2)
        > 8
        polys[1](2)
        > 8
        polys[2](2)
        > 8

The same thing happens if we use a list comprehension:

    
    
        polys = [lambda x: x**i for i in range(0,4)]
    

The trick is to add a default argument value to the lambda, which catches "i"
by value instead of by reference:

    
    
        for i in range(0,4):
            polys.append(lambda x, i=i: x**i)
        

And now we get the different polynomials we expected:

    
    
        polys[0](2)
        > 1
        polys[1](2)
        > 2
        polys[2](2)
        > 4
        polys[3](2)
        > 8

------
jwilk
The first example could be written as:

    
    
      colors = ["Goldenrod", "Purple", "Salmon", "Turquoise", "Cyan"]
      normalized_colors = map(str.casefold, colors)

------
weatherlight
I don't write Python. But I do program in a few different languages (Ruby,
Elixir, ES6, CoffeeScript, Rust...)

Is this a thing? Are Lambdas "Unpythonic?" or is the writer of the article
totally off base?

~~~
newfocogi
The author is entitled to his perspective, but I would equate this article to
JS articles titled "A framework author's advice to avoid frameworks". Its a
trendy article because it takes something that a lot of people use and
describes its weaknesses. Fun to read and argue with, but not a mainstream
idea.

------
jimmaswell
Law of headlines applies to the headline giving me a command too, I've
noticed.

------
LukeHoersten
The benefit of purely functional programming is that side effects can be
managed explicitly and checked by the compiler, speeding up code refactors and
making them safer. Using high order functions to save a few lines of code is
not the big benefit of functional programming and is more a superficial
feature more akin to syntax sugar. Same with list comprehensions. This seems
to be the case in many hybrid languages that borrow superficial purely
functional language features.

~~~
sinkasapa
It seems like if you want the expressiveness of the simply typed lambda
calculus, higher order functions are more than just syntactic sugar.

------
maximilianroos
I've struggled with this balance for a while.

I've come down to the view that _defining a function using `def` syntax adds
structure to a program_, even when it's not desired. It's multi-line, includes
an indent and a keyword. It look more like creating a `Class` than a `list`
for example.

As a result, in order to define a function _without_ adding structure people
fall back on lambdas.

I think the best resolution would be to embolden lambdas with some of the
benefits of functions - e.g. multiple lines

------
diminoten

        sorted_numbers = sorted(numbers, key=abs)
    

Need to remember that, I'm sure I've committed that sin!

------
Grue3
You can pry them from my cold dead hands. If functions are first class
objects, which they are, then they should not necessarily have a name, just
like not every object created during runtime is at some point assigned to a
variable.

------
agumonkey
for the record, the 'useless function call' paragraph is known as eta-
conversion in the FP world. see:
[https://wiki.haskell.org/Eta_conversion](https://wiki.haskell.org/Eta_conversion)

First line says:

    
    
        An eta conversion (also written η-conversion) is adding or dropping of abstraction over a function. 
        For example, the following two values are equivalent under η-conversion:
          \x -> abs x
        and
          abs

------
greglindahl
Python lambda expressions have the same problem that Fortran statement
functions have, that's why Fortran statement functions were abandoned a few
decades back. Not much new here.

