Tradeoffs of Lambda Functions
A bit of the content of this article was auto-generated by Microsoft CoPilot. I have removed parts that were sketchy or outright untrue.
TLDR;
I don’t recommend using lambda functions. They are too hard for other programmers to understand in my experience. In other words, they don’t lend themselves to readable code.
Alternatives you ask? Just use regular functions or methods!
Agree? Disagree? I’d love to hear your thoughts.
Introduction
What is code readability? Wikipedia defines it as:
In computer programming, readability refers to the ease with which a human reader can comprehend the purpose, control flow, and operation of source code. It affects the aspects of quality above, including portability, usability and most importantly maintainability.
see https://en.wikipedia.org/wiki/Computer_programming#Readability_of_source_code
Entire books have been written on this subject so I really can’t scratch the surface in a blog post. We will only address lambda functions for Python and C++ in this post.
C doesn’t have lambdas, but places you might encounter lambda functions include, but are not limited to: Java (https://stackoverflow.com/questions/25192108/what-is-the-breakdown-for-javas-lambda-syntax) and Excel and Google Sheets (https://www.vertex42.com/lambda/).
Code
Link to github code - Python:
keypuncher/Python/lambdas at main · mday299/keypuncher (github.com)
The above code has been tested on Ubuntu 20.04.
Tradeoffs
The trade space for lambda functions include (for Python and C++):
Python
Advantages of Lambda Functions:
Succinct code: Lambda functions can sometimes be defined in a single line of code.
No need for def or return: Lambda functions are a way to create small anonymous functions.
While this definitely makes code concise, the tradeoff is it makes it less readable. The return statement is a basic concept in computing!
Short-lived Functions: e.g., you need to define a function that is only used within a limited scope and doesn’t require reuse elsewhere in the code.
In my experience that’s rarely the case. If you insist on using lambda functions, it is probably best to use them in one utility library.
This can lead to a growing dependency on the utility (or set of utilities) that you choose to use.
At the risk of repeating myself -- just use functions or methods people!
Lend themselves to functional programming design: They are useful when used with functional programming tools in Python like when customizing the behavior of standard algorithms such as sort(), transform(), for_each(), map(), filter(), reduce() and so on.
My rebuttal: why not just do this with a function or method that uses map(), filter() or reduce(), etc.?
Disadvantages of Lambda Functions:
Limited to single expressions: They are unable to include intricate reasoning.
Readability: Using lambda functions can make the code harder to read and understand, especially for complex operations.
No recursion (in Python): lambdas cannot be used to define recursive functions. Recursion requires a function to call itself, and a lambda function cannot refer to itself.
C++:
Advantages of Lambda Functions:
Zero cost abstraction: Lambdas don’t cost you performance -- it’s as fast as a normal function.
Improves locality of the code: In C++03, you had to create functors or callbacks that could be far away from the place where you passed them as callable objects.
Example: C++ functor (AKA function object):
class Add {
public:
int operator()(int a, int b) {
return a + b;
}
};
int main(void) {
Add add;
int sum = add(1, 2); // Outputs: 3
}
Example C++ callback function:
#include <iostream>
#include <functional>
void process(int a, int b, const std::function<int(int, int)>& callback) {
std::cout << callback(a, b) << std::endl;
}
int add(int x, int y) {
return x + y;
}
int main() {
process(1, 2, add); // Outputs: 3
return 0;
}
In this example, process
is a function that takes two integers and a callback. The callback is invoked with the two integers as arguments.
Lambda functions are defined at the point where they are used, typically within a function call.
This has the disadvantage of variables sometimes going out of scope -- see Limited scope below.
See more on the types of locality on Wikipedia: https://en.wikipedia.org/wiki/Locality_of_reference
Recursion is possible with lambda functions in C++, but it’s a bit tricky because lambda functions are anonymous (they don’t have a name) and recursion traditionally relies on a function being able to call itself by name. You can achieve recursion in a C++ lambda function by using a generic lambda that takes itself as a parameter, but I have not independently verified this.
Disadvantages of Lambda Functions:
Complex to use: Developers find Lambda expressions are complex to use and hard to remember its syntax.
This is especially true for mutable lambdas. Lambdas typically are passed as const https://en.wikipedia.org/wiki/Const_(computer_programming), but you can bypass this restriction in C++ by adding the mutable keyword, allegedly. I tried to get this to work briefly but did not meet with success.
Limited scope: If you capture a local by reference in an async lambda, that local could easily be gone by the time the lambda runs. Your code could cause an access violation at run time.
Limited portability: As a C++11 feature, lambda functions may not be supported by older compilers or on all platforms
Code Complexity Metrics
Ways to measure code complexity include: (see What is Code Readability? (hashnode.dev)).
Formatting of Code: Includes indentation, spacing, letter case, etc.
Naming Conventions: The naming of variables, functions, classes, etc., can significantly impact code readability.
Nesting Levels: The levels of nested conditionals and loops can affect how easily a developer can understand the code.
Extreme examples of this include Never Nesting: https://www.youtube.com/watch?v=CFRhGnuXG-4
Function Complexity: This includes the length of a function and the number of parameters it accepts.
Cyclomatic Complexity: This measures the number of paths your code can take.
I took a deep dive on Cyclomatic Complexity last year. Among the resources I found are:
The Wikipedia article: https://en.wikipedia.org/wiki/Cyclomatic_complexity
A way to measure it exists for Visual Studio: https://learn.microsoft.com/en-us/visualstudio/code-quality/code-metrics-cyclomatic-complexity?view=vs-2022.
Halstead Complexity Measures: These focus on the different words (operators and operands) in your code’s language.
I haven’t actually studied these in depth, but here’s the Wikipedia article: https://en.wikipedia.org/wiki/Halstead_complexity_measures
Credits
Never Nester - https://www.youtube.com/watch?v=CFRhGnuXG-4
Lambdas in Java: Lambda Expressions in Java - Full Simple Tutorial (youtube.co