7 ms·
I always thought of that as a feature.
by diarrhea 5y ago
I always thought of that as a feature.
- mrweasel 5y agoExcessive usage of anonymous functions is a major pain. JavaScript developers for some reason almost refuse to name their functions, even though it would make reading and reusing code much easier. You can make lambdas in Python, but naming function is in my mind very much a "feature".
- tobr 5y agoHm, why is it more important to name a function than, say, an if statement?
- pjmlp 5y agoCode readability, specially if it spans multiple lines and has conditional code into it. Same applies to then and else branches. In a code review I don't what to read a thesis inside of a conditional branch.
- mrweasel 5y agoIf you just have a short one or two lines of code in a callback, maybe it's not so important. Where I have an issue is when you have five nested anonymous functions. Why wouldn't you break those out into named functions? Assuming you have five levels nested if, ignoring the fact that you might be doing something wrong, then you should name those, but that requires you to extract those code paths and put them into separate functions.
- roenxi 5y agoBecause an if statement is very constrained in what it can do. Every if statement has two branches (maybe one is a no-op) and one of them executes. If someone sees an if statement, it is implementing a fork in the code path. A function can do literally anything. Seeing a function call, it hasn't narrowed down the realm of what might be happening even a little bit. "lambda f:" gives away very little information about what is about to happen. "if f:" gives away some information about what is about to happen. Not a perfect rule of thumb, convoluted if statements do exist. But that is why functions are more in need of hints and comments than other control structures.
- chriswarbo 5y ago> "lambda f:" gives away very little information about what is about to happen. "if f:" gives away some information about what is about to happen. I think that's an unfair comparison: If 'the thing that happens' for "if f:" is 'branch on the truthyness of f', then I agree that's pretty clear. However, if we're going to ignore the content of the branches then we should also ignore the body of the function, so "lambda f:" is also pretty clear: 'parameterise with f'. If we're worried that "lambda f:" could do pretty much anything in the body, then we should also worry that "if f:" can do pretty much anything in the branches.
- roenxi 5y ago> If we're worried that "lambda f:" could do pretty much anything in the body, then we should also worry that "if f:" can do pretty much anything in the branches. That is the part common to both. If you take that common part out, you will be left with more information in the if statement than the function call. In the function, you know what arguments it (might) be interested in. You know they are evaluated. But in the if statement, you know what values it is interested in, usually get the same amoutn of information about whether they are evaluated, much clearer guarentees about how they are used and that the aspect of them that matters is their truthiness. Also the code might be about to branch. Put it this way - if(...) is a specific - and common - named function. It is a very easy argument to make that it is carrying more information about what is going on than a general anonymous function.
- Chris_Newton 5y agoIf you take that common part out, you will be left with more information in the if statement than the function call. I don’t think this argument generalises well. Once you start working with structured data and more complicated control logic, a generic loop with an arbitrary body often provides less immediate information to a developer reading the code than a function that explicitly names a specific pattern of computation like map or filter. There are many recurring patterns of computation that are much more specific than a generic map or filter, too. While imperative languages tend to have only a small number of built-in control structures, if we’re using higher-order functions then we can name as many of these patterns as we like. For example, suppose we want to generate a list of the first few triangle numbers, which is a sequence of the form: [0, 0+1, 0+1+2, 0+1+2+3, ...] Writing this in imperative Python code with a typical loop would give us something like this: triangle_numbers = [] total = 0 for n in range(6): total = total + n triangle_numbers.append(total) Here’s an idiomatic functional version, which is real Haskell code using a standard library function that captures this pattern of sequence generation in just five characters: triangle_numbers = scanl (+) 0 [1..5] Assuming you’re equally familiar with both programming styles, I think it’s fair to say that “scanl” tells you much more about what this code is doing than “for”. The inner function here is just addition in both cases. However, the code within the Python loop is allowed to do whatever it wants, so the reader has to recognise the pattern surrounding the addition. In the Haskell version, the function you pass to scanl must take an accumulated value so far and a next value as parameters and it must return the next accumulated value to add to the sequence being generated, and that is all it is allowed to do. So you just need to write (+), which is Haskell’s notation for what Python calls operator.add or an equivalent lambda, and that tells the reader exactly how the next accumulated value is being derived at each step to build up the sequence.
- mypalmike 5y agoA pure function (which a lambda typically is) answers a question. It's helpful to have words describing what question it answers. The next person to read the code will actually be able to read the words. An if statement is a lower level mechanism for directing the code path within a function. If it needs a name, wrap it in a function. I often do.
- HelloNurse 5y agoControl statements rarely "need" a name, but if they are significant enough to be named they can be named by extracting meaningful and somewhat reusable functions.
- tacostakohashi 5y agoAn if statement has a name, it's "if". It also has a condition and a body to execute conditionally. if (condition) body is just a goofy way of writing if(condition, body)
- pjmlp 5y agoFully agree, if you want to see that taken to the extreme, read Android code where people apparently only know about anonymous Java classes. Many Swing coders can also apply. It is so hard to actually pack the functionality into a separate piece of code. "Yeah, but then I have to look elsewhere" Really? What about when a method is full of stuff like that? Its readability is lost beyond hope.
- runald 5y agoHave you actually used other languages like clojure, haskell, kotlin, go or ruby? Python is the weird one here for not allowing multi-line lambdas. There are legitimate and actual useful cases for multi-line lambdas, disallowing them does more harm than good. If the idea of multi-line alone compromises readability, then why even have multi-line blocks at all? Function, loop and conditional blocks should all at most have one line each. I hope you can see the absurdity of this.
- chishaku 5y agoBeing weird isn’t sufficient evidence for being worse which is what you seem to be arguing. Why is having to name a function doing more harm than good? Your second paragraph is a straw man.
- runald 5y agoWeird as in it goes against well-established practices. If there are good, compelling reasons to disallow multi-line lambdas, other languages would have adopted it. But no, the opposite is happening, newer and recent languages actually have multi-line lambdas. Get this, even java adopted multi-line lambdas. Crazy right? What's wrong with everyone? There's nothing wrong with having to name a function. But there is something wrong with not having a choice and being forced to name a multi-line function. Just as you don't want to name every sub-expressions, you don't want to name every blocks of code. Unnecessarily naming things pollutes the namespace. Where's the strawman? The premise was that functions that spans more than a line should be given a name because they are "unreadable". I have showed that, by this logic, anything that spans more than a line is "unreadable". You can't make special exceptions and say this only applies to lambdas. Lambdas (aka higher-order functions) are blocks of code just as much as anything else.
- ehsankia 5y agoIndeed, there are many such explicit omissions in python (switch statements, assignment expressions (before 3.8), increment/decrement operator, etc) that may seem to some as a bug, but it really is an explicit decision and part of what makes Python the clean and readable language it is.