9.9 Summary
A defining characteristic of functional programming is the use of functions as values. Functions can be stored in data structures, passed as arguments to other functions, or returned as values by other functions. Functions that take functions as arguments or return functions as values are said to be higher-order functions.
Passing functions as arguments to other functions makes it possible to parameterize a higher-order function by the behavior of its function arguments—for example, a searching method parameterized by a search criterion.
To facilitate the implementation and usage of higher-order functions, many programming languages offer a syntax for function literals, which are expressions that evaluate to function values. This can take different forms, but a very common syntax is that of a lambda expression, which defines an anonymous function in terms of its arguments and return value.
A curried function consumes its first argument (or argument list) and returns another function that will use the remaining arguments (or argument lists). By currying, a function that uses a list of multiple arguments can be transformed into a function that uses multiple lists of fewer arguments. This facilitates partial application to some but not all of the original arguments.
In hybrid languages that combine object-oriented and functional programming, function values tend to appear as objects, and a function is applied by invoking a method of the object. Functions and methods are thus conceptually different: Functions are objects, which contain methods. Note that both methods and functions can be higher-order.
Hybrid languages define syntax to bridge methods and functions, specifically to build a function value out of code defined in a method. One example is method reference: obj::method represents the function x -> obj.method(x) in Java. Another is implicit λ-conversion: obj.method is transformed by the Scala compiler into x => obj.method(x) based on context.
Partial application is another convenience mechanism used to generate functions. It relies on placeholders—for example, “_” in Scala, it in Kotlin—that make a function out of an arbitrary expression and can be thought of as a generalization of currying.
The syntax used to implement function literals—lambda expressions, method references, partial application, etc.—can often be used in hybrid languages to create instances of SAM interfaces, which are interfaces and abstract classes with a single abstract method. This results in frequent use of lambda expressions as a replacement for more verbose mechanisms, such as anonymous classes, independently from functional programming patterns.
When a function value is produced from code in a function or method that refers to variables other than its arguments, the compiler needs to construct a closure. The closure associates the function being created with a lexical environment that captures those variables. This is necessary for a function value to be usable outside its defining context.
Programming with higher-order functions often involves a form of inversion of control. Control flow is embedded into a higher-order function, which then uses arguments as callbacks into the caller’s code. It is an effective programming style once mastered, but the resulting code is more abstract and can require some adjustment for programmers used to imperative programming.