Functional programming is an implementation strategy that focuses on the direct use of functions as first-class objects. This means that in a functional program you are allowed to create, store, and call functions and otherwise use them as if they were just another variable of the system. Functional code also simplifies programming decisions because it avoids changing state and mutable data. This type of functional manipulation allows programs to more closely express the desired behavior of the system and is particularly suitable to some application areas.
Functional programming is especially useful in the development of mathematical software and in the processing of large datasets, as is the case in the analysis options and derivatives. It can also be successfully used in the development of multithreaded systems, since it helps in the maintenance of lock-free code.
While the practice of functional programming was possible in previous versions of C++, such techniques have more recently been greatly improved with the adoption of the new language standards (from C++11 to C++20), particularly with the introduction of lambda functions. With lambda functions, programmers can now create temporary functions in place and pass them as arguments during the call to other functions. Such features have made it easier to apply functional programing techniques to C++ applications.
Functional objects: A functional object allows an instance of a class to be called with the same syntax as a function, by using the function call operator.
Functional templates: The STL has support for functional programming through the use of functional templates. With them, programmers can pass functions as parameters, as well as compose functions.
Lambda functions: With the introduction of C++11, a new syntax was created to represent unnamed functions, also known as lambda functions . You will see how to use lambdas in C++ and learn how they simplify the creation and maintenance of functional code.
Functional techniques for options processing: Throughout the chapter, you will see examples of how these functional programming techniques can be effectively used to solve some problems occurring in the analysis of options and derivatives.
Functional Programming Concepts
Functional programming has its roots in the analysis of mathematical algorithms, where functions are the main abstraction. Such functions are typically used to compute results based on mathematical properties of numbers. Functions can be used to express mathematical algorithms as well as used as an effective abstraction for the creation of complex algorithms in several areas.
In particular, functional programming uses functions as building blocks to create solutions for computational problems. Using this programming technique, you can call functions as well as perform operations on them, including composition, partial application, currying, and filtering, among others. You will see examples of these operations later in this chapter.
It is possible to compose functions to achieve complex behavior from a few simple base functions. Functional composition can be more easily done when functions are treated as a first-class object, instead of as an isolated element of the language.
Functional programming doesn’t depend on complex states stored inside objects. Functions are generally transparent, and they depend on arguments that are passed at each function call. In comparison, objects are complex and store a lot of context that may be hard to validate and understand. The use of functional programming techniques favors the creation of simpler code with less state, since the state needs to be passed at each function call.
Operations such as memoization can be easily performed when functions are first-class objects. With memoization, it is possible to cache the values of function calls, so that the next time a result can be immediately returned. This can be done because functions don’t store any mutable state.
No complex hierarchy of objects is necessary. Unlike OO programming, functional techniques are not based on hierarchies and therefore require no knowledge of the internal relationships of classes. Functions are independent of each other and can be applied in any sequence.
In the next few sections, you will see examples of these functional concepts applied to C++ through different techniques. First, you will see how to use function objects for this purpose. Then, you will see how to use external libraries such as boost::lambda. Finally, you will see how to implement functional programming techniques using C++ lambda functions.
Function Objects
The first step toward working with functional programming in C++ is to use a flexible representation for functions. One of the most common techniques for doing this is to use function objects. A function object (also known as a functor ) is a C++ concept that allows programmers to create class instances that behave as if they were functions. The key for this concept to work is the overloading of the function call operator (represented in C++ by a pair of matching parentheses).
The OptionComparison class is the main focus of this example, since it declares a data type that can be used as a comparison function.
The first part of the implementation contains a few standard member functions that are required by C++. The next part of the implementation, containing operator(), shows how the comparison functionality is handled by this class. In this simple case, the class considers the m_directionLess flag to determine if a less than test should be used. Otherwise, the function uses a greater than test and returns the results.
The first line of test_compare creates a new instance of the comparator object. Then, the code creates two SimpleOption objects and initializes them as necessary. The comparison object is then called as if it were a function, using operator ().
The strategy displayed here can be used to simulate functions with different signatures by creating the appropriate version of the operator (). Also, a single class can decide to implement several versions of the operator(), depending on the ways in which it expects to be called.
Functional Predicates in the STL
As you learned in the previous section, objects can be used to simulate functions in C++ through the definition (or overloading) of the function call operator. This flexible mechanism can be used to create code that behaves as a function but encapsulates complex properties, as any object can do.
List of Functional Templates Provided by the STL
Functional Template | Description |
---|---|
equal_to | Compares two parameters and determines equality between them. |
Greater | Compares the two given parameters and returns true if the first parameter is greater than the second. |
greater_equal | Compares the two given parameters and returns true if the first parameter is greater than or equal to the second. |
Less | Compares the two given parameters and returns true if the first parameter is less than the second. |
less_equal | Compares the two given parameters and returns true if the first parameter is less than or equal to the second. |
logical_and | Receives two Boolean parameters and returns true if both parameters evaluate to true. |
logical_or | Receives two Boolean parameters and returns true if at least one of the parameters evaluates to true. |
logical_not | Receives a Boolean parameter and returns true if the parameter evaluates to false. |
Plus | A functional template that receives two numeric parameters and returns their sum. |
Minus | A functional template that receives two numeric parameters and returns the first minus the second. |
Negate | A functional template that receives a single numeric parameter and returns the negative of that value. |
Divides | A functional template that receives two numeric parameters and returns the value of the first parameter divided by the second. |
Bind | Receives a function or functional object as a parameter and binds the parameters to that function to constant values or to variable placeholders. |
The goal of the functional objects included in the STL is to provide a set of basic operations for creating new functional objects. Notice that through the combination of the given objects, it is possible to create complex functions to encode application-dependent logic. You can freely combine these functional templates to define larger expressions in a way that represents the desired functionality.
Be aware of the differences between using functional objects and normal C++ operations. A C++ computation specified with operators such as * and + cannot be passed as parameters to other functions, because they are immediately executed in place. Functional objects, on the other hand, form expressions that can be passed to other functions. Moreover, the process of putting these functional objects together is performed by the compiler. This ability to create complex expressions and pass them to other functional objects and templates makes these STL templates useful for the purpose of functional programming.
Here, you first create a sequence of integer values and store it in the variable numbers. In this case, the code is taking advantage of the initialization syntax of C++11, which allows for the sequence type to be left unspecified, while the result is stored in a std::vector.
The next step is to call std::sort on the sequence of numbers. As you have seen before, the last argument of std::sort is a comparison function. Here, you can pass a functional object declared in functional.h, therefore freeing you from having to define a separate function.
This example shows you how to take two lists and perform an arithmetic operation with its respective elements. The operation in this case is the plus functional template, which adds two values and returns the sum. The first step is to create the two sequences. You can use the auto keyword to simplify the declaration of these sequences; they will be represented as vectors of integers. A result vector is also necessary, as declared in the next line of code.
The next step is to use the std::transform function to perform a transformation from the two source sequences into the destination sequence. Each step of the transformation uses the std::plus function. The result of this process is then sent to the standard output using the std::copy template function.
You could modify this example to perform any of the arithmetic or logical operations available in the functional header file, including adding, subtracting, multiplying, and dividing. More complex operations could be performed by combining these functions.
In general, the transform function template is very useful when you want to perform a common action to a list of elements. By using transform, you can reduce the number of explicit for loops in your code, making the resulting program easier to understand.
The Bind Function
In the last section, you saw that several common operations are provided in the standard library using the mechanisms of functional programming. With these templates, you can write transformations to lists of data without having to program explicit loops or use other imperative programming techniques.
However, just using the primitive operations such as subtract and divide is not enough to create complex application logic. Another thing that you can do using the techniques of functional programming is bind parameter values for a given function, so that you can have a new, modified function as a result.
Consider, for example, the std::plus<T> function provided in the functional header file. It can be used to add two numbers and can be applied to members of separate containers using the transform function template. A simple modification of this function is to have a constant number as the first parameter, so that the resulting function is in fact adding a constant value each time it is applied. Functional programming allows functions to be modified in this way, before they are applied to the required data.
The solution in the STL is provided through the std::bind function. With std::bind, you can bind a particular value to one of the arguments of a given template function. By doing this, you can create as many different functions as there are new combinations of arguments.
To use std::bind, you need to determine the function to be modified and specify one or more values that will be bound to the function arguments. Among these bound parameters, you can also refer to the arguments supplied by the user of the function, at the time that the function is called. These arguments are called placeholder arguments, and named as the special variables _1, _2, _3, and so on.
In this example, the goal is to use a modification of the std::plus function , so that each element of the list is added to the value 3, resulting in a new vector with the results. The example is similar to what you have seen in the previous code fragment, but the bind template now modifies the plus function.
The first two lines of the example are importing std and std::placeholders namespaces. The std::placeholder namespace allows you to write the name of placeholder variables _1 or _2. Then, the original list is created and a result vector is allocated.
The transform function performs the desired changes, and bind is used to create the operation applied to each element of the list1 vector. As seen in the previous example, there are two arguments for std::plus. These arguments need to be specified in sequence. This is indicated with the second and third parameters of std::bind. The first argument is supposed to be the placeholder for the first parameter. The second argument is bound to a constant number.
This assumes that SimpleOption contains a member function that calculates the probability that a particular option will be in the money, given a number of days before expiration and the current underlying price. Moreover, the goal is to create a function that will receive a vector of options and return the associated probabilities for the specific case of two days before expiration. The function is called getInTheMoneyProblExample in the previous fragment.
To do this using the STL functional algorithms, you need to find a way to express the desired condition as a functional object and pass the resulting object to std::transform. This can done with the help of std::bind. The idea is to use std::bind to bind the value of the first argument, which in this case is the number 2. Then, the placeholder _1 indicates that the argument passed to the resulting function is used as the second argument to getInTheMoneyProbability. The bound function is then saved to a variable called inTheMoneyCalc and used as an argument to transform, applied to the options vector.
Lambda Functions in C++20
As you saw in the previous sections, classes, templates, and objects can be used to represent functions and other functional objects. Unfortunately, using classes for functional programming requires you to define a separate function outside of the current place where it is being used, thus making the process more difficult than it needs to be. Functional templates such as std::plus and std::multiplies help make this easier, but it is still not as easy as writing plain C++ code.
Other languages such as Lisp and Python have simplified this task with the concept of unnamed functions, also called lambdas . These unmanned functions can be passed as parameters to other functions and objects and can be freely combined into more complex functions. This way, functional programming techniques become much easier to implement and test, when compared to languages in which functions can be created only as a static entity.
One of the big changes since C++11 (and improved in C++20) was the introduction of lambda functions as a syntactical element. With the addition of lambda functions, it is now possible to create unnamed functions that can be saved as variables or passed as parameters to other functions. This considerably simplifies the task of applying functional techniques in C++ programs, as you will see in the next few examples.
A C++ lambda is a piece of C++ code that can be saved and/or passed as a parameter to other functions. With lambdas, the compiler has enough information to understand that the function will run later, probably in an environment that is dependent of the current function.
Lambda functions can also refer to variables that have been declared outside the block of the lambda function. This makes them much more convenient than standard functions, which are independent of the surrounding variables. This process is called lambda capture, and it allows a lambda function to access the data stored even in a local variable, after the current function has returned.
Lambda capture by value: Allows lambda functions to use the value stored in a variable that is accessible at the moment of the lambda declaration. The value can be used even after the original variable no longer exists. This is indicated by adding the name of the variable inside the square brackets that introduce the lambda function.
Lambda capture by reference: This allows a lambda function to modify the variable itself, instead of just using its value. This type of capture is indicated with an & operator before the name of the desired variable.
A lambda function can also be passed as an argument to other functions. When this happens, the compiler creates a template object of type std::function<> that stores all the information used by the lambda function. You can create new functions that receive lambdas and freely use them in your code. The compiler will automatically convert a lambda into an object during the function call. Consider the following example:
Complete Code
Conclusion
Using templates is a good way to organize your code into generic functions that work across different data types. However, it’s only when you start to compose these functions that you start to reap the benefits of a functional programming style. Functional tools in the STL and other libraries allow programmers to use functions as first-class objects.
In this chapter, you learned a few of the techniques available for programmers who want to explore functional programming in C++. Some of these techniques include the use of functional objects, which implement the function call operator to simulate native functions. The STL provides several template functions that support the use of functional objects.
You have also seen how to create and use lambda functions, a new syntactical element introduced in C++11. With lambda functions, programmers can create unnamed functions that can be saved as variables or passed as parameters to other functions. Even more interestingly, such lambda functions can refer to variables that occur in the environment in which they were created. In this way, lambda functions reduce the need to create additional classes just for the purpose of simulating function calls.
This chapter concludes the book’s overview of C++ programming techniques used on derivatives programming. In the next chapter, you will start to learn about mathematical tools that can be used to price and analyze options and other derivatives. In particular, you will learn about linear programming methods and their C++ implementations.