Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Understanding Lambda Expressions

DZone's Guide to

Understanding Lambda Expressions

Lambda expressions are code blocks that can be assigned to certain variables, and they've been around a while. Here's a breakdown of lambdas, with examples in several languages such as Python, Ruby, and Scala.

· Performance Zone
Free Resource

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

Lambda expressions (or lambda functions) are essentially blocks of code that can be assigned to variables, passed as an argument, or returned from a function call, in languages that support higher-order (first-class) functions. They have been part of programming languages for quite some time. Some examples include Smalltalk, Lisp, Ruby, Scala, Python, and, more recently, Java and JavaScript. Here are some examples: 

Python:

square = lambda x: x**2
square(6) //->; 36

Ruby:

square = lambda { |x| x * x }
square.(6) #-> 36 

C#: 

del square = x => x * x;
square(6); //->36


JavaScript (fat-arrow functions): 

var square = x => x * x;
square(6) //-> 36


Lambda expressions are also known as closures or blocks. If you look closely at the Ruby code, you can see the declaration of a “callable” block of code (encapsulated in { } braces) that is assigned to a variable and run. Lambda expressions are also called closures because they access state from its surrounding lexical scope. This is unlike function pointers in C, which can also be passed around but do not carry (or close over) their lexical scope.

Most people perceive lambdas simply as an anonymous function, but when you study them closer (perhaps from a functional programming point of view), lambda expressions carry a lot more meaning and are a lot more powerful than you think. To speak about this more concretely, I’ll refer to JavaScript’s “arrow” syntax shown above.

Syntax

The most notorious difference is in syntax. Lambda expressions offer a syntactical advantage of over a traditional anonymous function declaration

function (x) { return x * x };

by removing a few artifacts like the function and return keywords, which makes these expressions feel more like just callable blocks of code. This syntax also makes your code a lot more succinct when used in conjunction with higher-order functions like mapreduce, and filter:

range(1, 5).map(x => x * x); //-> [1, 4, 9, 16, 25]

Singularity

The benefits of using lambda expressions extend way beyond just syntax. Lambdas encourage you to practice the first of the famous SOLID principles of software design: Single Responsibility. Applied to functions, single responsibility just means that functions should have a single purpose. While you can have lambda expressions contain multiple statements

(x, y) => {

  // do something with x and y  

  // statement 1 

  // statement 2   

  return result;

}

most of its usage derives from being inlined as a function argument into larger expressions. This means that you can create several small lambda functions that do exactly one thing, and combine them together to form whole programs:

range(1, 5)
   .filter(x => (x % 2) === 0)
   .map(x => x * x); //-> [4, 16]

But to really understand why they are so important, we need to examine lambda expressions from a functional programming point of view. 

Pure Functions

Functional programming treats functions as pure mathematical relations or mappings between sets of types; in this case the set of inputs (domain) and output (range or codomain). Hence, you can view any functions as a mapping of types. For instance, the squared function is a mapping from Number to Number






You can also map into any types, including user-defined types like a Long number to an Account object:

As you can see from these diagrams, mathematically speaking, a functions output must be solely derived from its input and not from any other state outside of the function’s scope. This absence of side effects is what determines that a functions is pure, as all functions in FP should be. Lambda expressions encode this pretty well into its syntax by using the logical implies symbol “=>” to mean that a set of inputs implies a certain output

Referential Transparency

We can extend this concept of purity to a mathematical principle called referential transparency, which means that a pure function can always be substituted for its computable value without altering the meaning of an expression. For a concrete example, consider a pure function like size:

var size = (arr) => !arr ? 0 : arr.length;

And a function average

var average = (arr) => Math.round(sum(arr) / size(arr))

For an input such as [80, 90, 100], size will consistently return the value 3. Because this function always returns the same output on the same input, it’s said to be referentially transparent — actually the entire program above is. That means that I can use size(arr) interchangeably with the value 3 in the expression above, without altering its meaning. This mathematical quality makes our programs much easier to reason about and easier to trace on any given input. Indeed, these are very simple functions yet the concepts apply equally as well to the most complex algorithms. Referential transparency means that I can treat functions truly as black-boxes because I can predict a function’s outcome based solely on the input provided. 

Consequently, it’s easy to see that under referential transparency there’s no room for either no-argument functions or void functions; mathematically speaking, either of these cases implies the empty set “∅” in the domain or codomain, respectively. Take the following example that uses the empty set notation in place of the arguments list: 

var hello = () => console.log('Hello!');

Zero-arity functions or void functions always imply that side effects are taking place, since no other real work could be done without accessing resources outside of its own scope. Lambda expressions reveal this mapping more closely. 

Composition

All of these principles serve an important purpose: to facilitate function composition — the epitome of functional programming. Composition is used to orchestrate the execution of functions in a loosely coupled manner where the output of one is used as the input to the next. 

f o g (x) = f(g(x))

At a fine-grained level, function composition resembles the “coding to interfaces” principle because it treats functions as a black boxes with clear inputs and outputs. For example: 

var str = 'We can only see a short distance

           ahead but we can see plenty there

           that needs to be done';

var explode = str => str.split(/\s+/);

var size = arr => !arr ? 0 : arr.length;

var countWords = compose(size, explode);

countWords(str); //-> 19

I can use composition to join independent functions and create entire programs based on these simple concepts. Lambda expressions encourage us to create programs that are declarative,  modular, and reusable at a very fine-grained level!

Evolve your approach to Application Performance Monitoring by adopting five best practices that are outlined and explored in this e-book, brought to you in partnership with BMC.

Topics:
lambda expressions ,programing ,programming practices

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}