Lesson 3

Welcome! Today, we're diving into an essential concept in functional programming: the **Functor Design Pattern**. You might ask, "What is a *functor*, and why should I care?"

As you might remember we talked about functors in the previous units. In C++ a "functor" usually refers to a class implementing the `()`

operator, which can be called like a function. However, in more general terms, a functor is a different concept.

Generally speaking, *Functors* allow you to map a function over a structure, making your code clean and modular. By the end of this lesson, you'll know how to create and use *functors* in **C++** to transform data structures effectively. Let’s get started!

First, let’s clarify what a *functor* is. In **Functional Programming**, a *functor* refers to an object, such as a class or a template class, that provides a method (`transform`

or `map`

) to apply a function to some specific element.

In more general terms, consider a template class `F`

. This template class is considered a functor if it defines a `map`

(or `transform`

) method. This method should take two arguments:

- A value of type
`F<T1>`

. - A function
`t`

that transforms a value of type`T1`

to a value of type`T2`

.

The `map`

(`transform`

) method then applies the function to the value inside the structure `F`

and returns a new structure `F<T2>`

with the transformed value.

Let's see an example to understand this concept better.

First, let’s define the `Functor`

class and understand each line of code step-by-step.

C++`1#include <iostream> 2#include <optional> 3#include <functional> 4 5template<typename T> 6class Functor { 7public: 8 // Define the transform method 9 template<typename U> 10 std::optional<U> transform(const std::optional<T>& value, std::function<U(T)> func) { 11 if (value) { 12 return func(*value); 13 } else { 14 return std::nullopt; 15 } 16 } 17};`

`template<typename T>`

: This specifies that`Functor`

is a template class parameterized by`T`

, which represents the type of the value inside the`std::optional`

.`template<typename U>`

: This specifies that the`transform`

method is itself a template method, allowing different types for the input (`T`

) and output (`U`

) elements.`std::optional<U> transform(const std::optional<T>& value, std::function<U(T)> func)`

: This is the declaration of the`transform`

method.- It takes two parameters:
`const std::optional<T>& value`

: A constant reference to an optional holding an element of type`T`

.`std::function<U(T)> func`

: A function object taking an input of type`T`

and returning an output of type`U`

.

- It returns an optional containing a transformed element if the input optional has a value, or
`std::nullopt`

if the input optional is empty.

- It takes two parameters:

The `transform`

method implementation is key to understanding how the functor operates:

C++`1if (value) { 2 return func(*value); 3} else { 4 return std::nullopt; 5}`

`if (value) { return func(*value); }`

: If the input optional has a value, apply the function`func`

to the dereferenced value and return the result wrapped in an optional.`else { return std::nullopt; }`

: If the input optional is empty, return`std::nullopt`

.

This way, our functor can apply any function to any optional value safely, ensuring no problems appear.

Now let’s see how to define a simple function that can be used with the functor:

C++`1// A simple function to be used with the functor 2int square(int x) { 3 return x * x; 4}`

In this example, we have defined a simple function `square`

that takes an integer `x`

and returns its square.

Lastly, let’s incorporate the code into the `main`

function to see the functor in action:

C++`1int main() { 2 Functor<int> optionalFunctor; 3 4 // Define an optional integer 5 std::optional<int> opt = 5; 6 7 // Use the transform function to square the value if it exists 8 std::optional<int> result = optionalFunctor.transform<int>(opt, square); 9 10 // Print the result if it exists 11 if (result) { 12 std::cout << *result << std::endl; // 25 13 } else { 14 std::cout << "No value present" << std::endl; 15 } 16 17 return 0; 18}`

`Functor<int> optionalFunctor;`

: We create an instance of the`Functor`

class specialized for`int`

.`std::optional<int> opt = 5;`

: Define an`std::optional`

holding an integer value.`std::optional<int> result = optionalFunctor.transform<int>(opt, square);`

: Use the`transform`

method to apply the`square`

function to the value inside`opt`

and store the result in`result`

.- The
`if`

statement and`std::cout`

print the transformed value if it exists, showing the squared value`25`

.

In this lesson, you've learned what *functors* are and how to use them for transforming data structures in **C++**. *Functors* help keep your code modular and reusable, making it simpler to apply functions across various containers.

Next, you'll have the chance to put this knowledge into practice. You'll be asked to create your own *functors* and apply different transformation functions to various containers.

Thanks for staying engaged throughout the lesson, and let’s move on to some hands-on practice to solidify your understanding!