In this series Eonics consultant Rutger van Velzen leads us down the rabbit hole of Functional Programming. In this third part he introduces first-class functions & higher order functions and deferred execution. Links to the other parts of this series can be found at the bottom of this article.
First-class functions are also known as anonymous functions. Another name for them are lamda expressions, but I consider this term incorrect. The true definition of a Lambda expression is fuzzy, probably because it shares its name with Lambda calculus. Even though first-class functions are largely based on the concept from lambda calculus, few functional programming languages match the literal description to make it a lambda expression. In the next chapter we will look deeper into this.
A definition that I like a lot is the following: “First-class functions are functions treated like data.” While First-class functions are nameless and thus anonymous, they are nameless in the scope of functions. But first-class functions can be stored in fields and be referenced, just like we do with data.
Because first-class functions can be stored in fields or variables you often have to declare them before referring to them, just like you would do with data. While structuring a class it is common practice to have the smaller more detailed functions tugged away at the bottom. Working with first-class functions this is the opposite; the deepest nested one could be found on top. Reading from bottom to top is quite common in functional programming.
Treating functions like data starts to become concrete when you work with higher-order functions. A function can be considered higher-order when it can take and/or return another function. First-class functions are higher-order as well, making this construction extremely flexible. This technique is without a doubt the greatest selling point of functional programming. All your logic suddenly becomes polymorphic!
A commonly made joke is that in functional programming all design patterns are replaced with just functions. Although the joke is not wrong, it is not right either. The essence of the joke is true; implementing design patterns is more straightforward when using higher-order functions, there are just fewer pieces at play due to the extra abstraction level.
Deferred execution is a strong selling point of functional programming. Where lazy evaluation aimed to memorize answers instead of redoing the same task over and over; deferred execution’s goal is to wait until the last possible moment before doing something.
Let’s say you have a data-object that is often used by other parts in the system. And that other class is only interested in a few fields. Instead of creating the data-object eagerly by fetching all the fields from the database on construction; you do nothing. And even when the other class specifically requests a field, you return a reference to the function. Remember, we are trying to be lazy here. The class can say it needs a value, but is it actually using it, or just passing it to yet another class?
It is common for a user to log-in and just check their account balance, so why construct a data-object with all their banking and contact details? If you wait long enough with the execution of a function, you probably never have to run it at all. So pass the function through the system, all the way to the top; instead of its return value.
Functional programming: the complete series
New articles in this series will be added to this list as soon as they are published.
- Part 1: historical context
- Part 2: pure functions & lazy evaluation
- Part 3: first-class functions, higher-order function and deferred execution
- Part 4: coming soon!
For feedback, questions or comments feel free to drop Rutger an e-mail.