An Overview of Elements of Functional Programming in C++

by Sep 17, 2017

Hello everybody!

This article is a brief introduction to the functional programming style when using an imperative language like C++. In particular, we will use C++Builder, a C++ development environment that can target many platforms, to show examples for how functional programming can work.

I'll start with some arguments about the role of functional programming in the object-oriented paradigm. Many of the statements here are controversial, so do not forget that "in a dispute, truth is born".

After reading this article you will get an idea of some new syntax features that are available, so get ready for surprises. Additionally, the information provided should be enough to understand the examples, and to fill in the gaps there will also be some useful links.

I’ll warn you now that this material is intended to arouse interest and encourage you to ask questions rather than to give you answers. For beginners (in functional programming as well as in  any other field) it is useful to be deeply interested in the subject matter.

Who should read this?

If you know C++ but are not familiar with functional programming,

or

if you write functional programs and would like to know what tools C++Builder provides for this purpose today,

or

if you do not know neither C++ nor functional programming, but want to get acquainted with them. (Programming skills are necessary, of course.)

Even for gray-haired C++ developers and experienced functional programmers it will be useful to read this article and shoot few critical arrows – because this action tones up the organism and increases self-esteem, that, in turn, will encourage the creation of many new excellent programs.

 

Part I.
About lambda functions, functors, which in fact are not such and a bit of other functional philosophy
 

Introduction

 

Functional programming was born and developed in academia, but lately it is gaining popularity in applied programming. Its origins lie in lambda calculus—a formal system developed in the 1930s—functional programming and many functional programming languages have lambda calculus in their makeup.

In the functional programming paradigm, the output value of a function depends only on the arguments that are input to the function; imperative programming in contrast changes the state with commands in the source code. Imperative languages include functions too, but they have a sense of subroutines. Object-oriented programming (the most popular paradigm) operates with objects, which have methods and internal state, where any method call can change the state despite the fact that outwardly it looks like a function call with some arguments in the mathematical sense. In contrast to this, the functional programming paradigm guarantees an identical result for a repeat call on a function with same arguments. Functional programming eliminates the headache of synchronization and offers an elegant solution to the problem of parallelizing recursive calculations.

“Everything new is a well-forgotten old” remarks the old Russian saying. Seems, this “new old” paradigm promises to make understanding and predicting the behavior of a program much easier. Does it really?

Unfortunately, in the real world everything is not so simple. Functional programming is good for academic applications, but it often faces natural difficulties in commercial programming. Malte Skarupke in his post “Functional Programming Is Not Popular Because It Is Weird” gives  discussion and interesting example of this problem:

To talk about functional programming let’s bake a cake. Taking a recipe from here, this is how you bake an imperative cake:

  1. 1. Preheat oven to 175 degrees C. Grease and flour 2 – 8 inch round pans. In a small bowl, whisk together flour, baking soda and salt; set aside.
  2. 2. In a large bowl, cream butter, white sugar and brown sugar until light and fluffy. Beat in eggs, one at a time. Mix in the bananas. Add flour mixture alternately with the buttermilk to the creamed mixture. Stir in chopped walnuts. Pour batter into the prepared pans.
  3. 3. Bake in the preheated oven for 30 minutes. Remove from oven, and place on a damp tea towel to cool.

I’d take some issue with the numbering there (clearly every step is actually several steps) but let’s see how we bake a functional cake.

  1. 1. A cake is a hot cake that has been cooled on a damp tea towel, where a hot cake is a prepared cake that has been baked in a preheated oven for 30 minutes.
  2. 2. A preheated oven is an oven that has been heated to 175 degrees C.
  3. 3. A prepared cake is batter that has been poured into prepared pans, where batter is mixture that has chopped walnuts stirred in. Where mixture is butter, white sugar and brown sugar that has been creamed in a large bowl until light and fluffy…

 Writing functional code is often backwards and can feel more like solving puzzles than like explaining a process to the computer, he explains.

Programming, as a rule, involves some modeling of reality. At this time, modeling always has a tinge of subjectivism – it depends on what researcher wants to perceive.

Let's think about how much functional programming is natural.

The way to influence to the external world through the own building actions arose in the process of evolution of multicellular organisms millions of years before the idea of an executing an instructions by machine. While plants have adapted to changes in the environment passively, i.e. morphologically, other organisms have chosen to actively change the reality around through their own bodies.

First, unconditioned and conditioned reflexes were formed, but with the improvement of the animal's brain, behavioral patterns became more and more complex, until finally a human with his ability to think abstractly came to the scene. Reflexes have not disappeared, behavioral models have become extremely complex for its imperatively description.

Similarly, we can observe the evolution of other complex adaptive systems, for example, evolution of the social development of mankind from authoritarian control to a set of public institutions functioning independently.

 So, is it really functional programming so weird?

I could answer “Yes!” Imperativity implies the presence of a certain recipe for achieving the intended goal step by step while the concept of functional programming is counter-intuitive when we are talking about some algorithm. It’s not how a person thinks about their activities. This is not how we think of interaction with either people subordinate to us, or with a machine that fulfills our will. When we set a goal, we need to understand what we need exactly to do in order at the way to achieve this goal.

At the same time I could say “No!” A person learns the unknown world with the help of his senses and muscular efforts functionally. Awareness of the relationship between events of the surrounding world, his own actions, and the consequences that result. Only by establishing strong functional connections between the objects of the external world, we can think of our impact on this world and to plan this impact step by step.  

The unknown became the known functionally – this is the most natural way. If you ask a small child about a cat he will call him “meow”. The child only begins to comprehend the world around him as he knows it functionally, until later he is able to build hierarchies and associations of objects. The first thing that a child learns about a cat is that the cat can meow, and that is the most important thing for him. Just the functionality of the cat, not the state, or the belonging to a biological species hierarchy, is what’s important to the child.

If a mouse reveals itself in your home, the first thing you will think is that you should get rid of this problem. At first you won’t realize that you need exactly a cat, instead a mouse trap, poison, or some technical tricks to scare off rodents.

If you just need to task someone with resolving this problem, then you do not have to worry about the details. But if you have to get a cat to resolve the problem, then you need to coordinate the pet with family members, feed it, and give it a place to live.

Using this analogy I want to show that the functional approach works well, while abstraction allows for it. If our "smart house" were able to remove mice as soon as we expressed concern about their presence, then this would be a “functional paradigm” problem for us. But it's still not what we actually have currently, so the functionality paradigm looks rather natural in future for its applications in programming, as soon as the usage of the abstraction levels will require it.

Note that your technical specification is often a description of some information system in a functional style. Imagine a time when computers will generate the program code by itself, having received on the input the properties of the new program: this way, even description in the style vividly outlined by Malte Skarupke in his example about the pie will no longer seem so "weird"!

Approaches to the functional solution of problems differ from what we are accustomed to seeing in imperative algorithms. We describe the dependencies and thus reap the fruit without toil instead of describing the procedure to attain the goal step-by-step.

Suppose that we are aiming to program the process that occurs from striking a golf ball until when it finds the hole. Developing an imperative algorithm we would calculate the launch angle and speed after impact. Additionally we would take into account possible obstacles, and apply logic related to choosing alternative shot paths. In the framework of a purely functional paradigm, we could change the course design so that the ball avoids any obstacle while finding the hole. In terms of physics, we would create a minimum potential for the gravitational field at the target point (and the potential function decreases monotonically throughout the path of the ball to the hole).

Miracle? This can not be true in reality?

Simply choosing the dominant paradigm entirely depends on the program task being solved. Many engineering calculations of past centuries boiled down to similar "miracles" until there were automatic machines that could step-by-step execute the will of man. For example, the clockwork mechanism did not assume the execution of commands, but instead the gear ratio of the gear wheels had to be observed to keep the clock in step with time and even perform certain actions in a timely manner.

What do we care about if we are doing imperative programming? The fact is that the elements of functional programming have already managed to take their place in imperative languages like C++.

C++ inherited the syntax of one of the earliest high-level languages—C. C++ has an impressive portfolio and remains a modern language today despite its decades long history. Many popular languages (like Java and C#) have similar syntax, inherited from C++.

If you are a professional programmer, or even an engineer working in information technology in some realm, it will be extremely useful for you to watch in which direction C++ moves. Even if you do not use C++ in practice, having some understanding of it is comparable to how people in the medical field should have some basic understanding of Latin.

It’s true that the syntax of living C++ bring up an association with dead Latin. C++’s syntax is backwards-compatible, and new features like lambdas can look odd in C++ code. It is a little like Latin being the basis for modern Romance languages. Let's take into account the historical role of C++, a language that has always been at the forefront of programming development, and from whose mistakes C# and Java have learned. C++ is definitely still at the forefront, and the arrival of functional capabilities in imperative languages is no exception here.

However, do not think that using C++ you can write programs in a purely functional style — for functional programming there are specialized tools! Instructions taken separately can be represented in functional programming terms, and it has been evident for a long time that we come across imperative programs with pieces of code that effectively in functional style.  

For example, callback functions are widely used in procedural programming for decades. Development, however, does not stand still and the object-oriented paradigm has left its mark. Now we have at our disposal lambdas, functors, and the STL; they go well with some elements of functional programming style if we deeply understand the principles and remember the pitfalls. In fact, why don’t we describe relationships between objects and let the std::algorithm do all the work instead of performing operations step-by-step?

The technique of using STL algorithms often resembles functional programming. Not on the scale of the application, but on the scales of a single instruction at least. Not only because it involves manipulating functions in the style of a functional paradigm, but also because the use of closures and currying becomes natural in this case. It does not look like an ugly patch on the OOP concepts. On the contrary, the newest functional possibilities allows you to write cleaner code, respecting OOP dogma, focus on algorithmization and maintaining good code readability.

It's time to move from words to deeds code. Let’s see how it works in C++.

 

 

Examples

To demonstrate the code examples I will use C++Builder Starter. You can download free this IDE from the Embarcadero website. This will allow us to compile code for the Win32 platform.

If you suddenly want to test this examples on a different platform, you will need a fully functional edition of the C++ Builder. Most of the examples require the creation of a console application project (ie Windows or macOS only), as shown at pictures below, unless otherwise specified, but if you make a UI app, you can also run them on iOS and Android. The following assumes a plain console app for Windows.

Functional objects

Let's move on to the example of our rodent problem. Again, imagine that you have a mouse problem you decide to solve with a cat. If you decided to get a cat only after your mouse problem presented itself, then most likely you are not a big cat fan; I will assume that the cat interests you primarily as a means of fighting mice. Instead of a cat, a mousetrap would have suited well if you’d given preference to it (although you need to have a good imagination to find a common hierarchy between the device and the pet). To solve your problem, you are primarily interested in the functional characteristics of the cat – its ability to catch mice. That desired characteristic does not prevent the cat’s meow, it having ears, paws, and a tail, etc. Thus, the cat is considered as a functional object in the your rodent control program.

A fat cat does not catch mice

Let's think, can the individual qualities like hunting skills and the health of the cat affect its effectiveness in hunting? In the real world, the answer is always "yes." We can abstract from this situation that as long as we just need the function of mice catching, and we do not think about exactly how the cat will do it. Additionally, you will have to take care of the cat, thus maintaining its internal state.

A distinctive feature of a functional object is its ability to possess an internal state, this makes it representative of the object-oriented world. In short, a functor is an opportunity to gracefully match both concepts in one piece of code. However, if your functor has an internal state, it should be very clear for you what will happen when you call its functional part.

You can stumble upon the pitfalls if you pass a functor to be used in an external algorithm. You can not be sure that your intuitive understanding of the logic of making a copy or calling a functor is the same as the actual one.

Old-style function objects

In C++, a function object is any object which allows a call when its name mentioned with function invocation semantics, ie parentheses. An ordinary function is a special case of a functional object too,  but hereinafter we mean  this term in the next narrow sense:

Function object (functor) is an instance of a class that defines operator().  This object is used to invoke the function to which it points. From the outer code view this object behaves like a function so this circumstance allows its use where function object types are expected due to function-to-pointer implicit conversion.

General syntax pseudocode is:

class functor_class {

 public:  return_type operator()(<list of parameters>) {    …   }

 

The using of a functional object syntax makes it possible to achieve the equivalence of the following instructions

var = f(arg);

var = F(arg);

 

in case when the code in the building of F::operator() is equivalent to the function f building.

For example, if we have the following declarations of functor and function:

return_type f(arg_type arg);

and

class F {

 public:  return_type operator()(arg_type arg) { return f(arg)  }

 

In other words, if we have some function f and functor F which operator() building declared as {return f(<list of parameters>);}  then the call of F(<list of parameters>) will equal f(<list of parameters>) call.

 

This means that when the name of an object of this type is mentioned in the context of parentheses a certain method is called, which looks externally like a function call with a matching name, but in fact there is a call to the object's functionality. So when your child asks you to run a cartoon about Tom cat, The Tom() syntax means that Tom cat will chase Jerry mouse.

“Function objects” vs “Functors” terminology

Nicolai Josuttis in "The C++ Standard Library" equates "function object" and "functor" terms even though the term "functor" does not appear in C++ Standard and mathematically this is not a correct definition. Despite that, we can use it in most general sense as “anything that can be used as a function” or “class represented by functional object”.

Generally speaking, some ambiguity is present in the whole terminology of functional objects. Ordinary functions are functional objects, and at the same time they are opposed to them.   Furthermore, a functional object is an instance of a “functional class”, in the declaration of which is the main part of our work.

But what is even worse is that the term “functor” has a meaning in functional programming, which may well come in handy in imperative C++.

This is a widespread terminological misconception. Perhaps it's too late to try to change something here. A new independent terminology could be a solution, but that's not what we can do now. All we have to do is at least be consistent everywhere.

 

If we use the term "functor" everywhere in this article to mean a "functional class" that is, a class with a defined call operator. A functional object usually means an instance of such class, unless it is explicitly indicated that this is a functional object of a general kind (that is, it can be a global function, a lambda or a member function).

Example

Let's take a look at this small console application. Suppose we need to generate an integer series by placing it in a vector. We will enter the range limits and calculate the number of elements in the series falling into the range.

The primary task is to fill the vector with integers. For this, I want to use the std::generate.  It is necessary to describe the function that will generate the next element of the sequence. We will fill the range with numbers equal to the element vector index—from 0 to size 1—but in a more general case, the elements of the series can be computed in accordance with complex dependencies. Using a global function is not a good solution since using global variables may be necessary.

We can create a functor class Generator  that will contain the variables associated with these calculations. Even in our simple case, we will use a private field for the sequence number to calculate the new element:

class Generator {

private: int n = 0;

}

 

Next, we need to determine the sequence of actions that will occur as a result of calling a functional object of this class. The signature of the std::generate function requires us to return the next element for the vector, after which we can simply increment the internal field.

public: int operator()(){return n++;}

 

When we create an instance of the Generator class

Generator gen;

 

its internal n value will initialized by zero.

 

Note that we have the vector with pre-defined size:

 

vector<int> v(m);

 

So, when we and pass it into the std::generate function,

 

generate(v.begin(), v.end(), gen);

 

it means that gen()  will called for every of the m vector elements and every element will assigned to the returned value. Whereas the n value increments every time when call occurred, our vector will filled by values from 0 to m-1.

But our main goal is to count the elements of a vector that satisfy a certain condition. The simplest and most concise way to implement this is to use std::count_if. To do this, we need to define a bool function defined on vector elements called a predicate and pass it to std::count_if. Predicate will return ‘True’ or ‘False’ for every element of the vector, and plays the role of binary filter: count_if will calculates the number of the elements of the vector for which passed predicate have ‘True’ value.

All we need is to declare a class with a defined operator() which receives an integer value in the input and returns a boolean value for it, i.e. functor with predicate functionality:

class Predicator {

Predicator(int a, int b)

bool operator()(int n)

{

return (_a <= n) && (n < _b);

}

}

Thus, we can create a functional predicate object and pass the range boundaries directly in its constructor, after which it remains only to use std::count_if with this predicate:

Predicator prdobj = Predicator(lowerBound, upperBound);

int result = count_if(srcVec.begin(), srcVec.end(), prdobj);

 

In addition, a functor class Printer  defined here that outputs the contents of the vector for use in conjunction with std::for_each. Generally speaking, this technique does not seem to be the most suitable here, but since we are talking about functors, you can consider it another example.

#pragma hdrstop
#pragma argsused

#ifdef _WIN32
#include <tchar.h>
#else
  typedef char _TCHAR;
  #define _tmain main
#endif

#include <iostream>
#include <vector>
#include <algorithm>


using namespace std;


class Generator {
	private: int n = 0;
	public: int operator()(){return n++;}
};


class Predicator {
	private:
		int _a;
		int _b;
	public:
	bool operator()(int n)
	{
		return (_a <= n) && (n < _b);
	}
	Predicator(int a, int b)
	{
		_a = a;
		_b = b;
	}
};


class Printer
{
public:
	void operator() (int value)
	{
	  cout<<value<<" ";
	}
};

 int _tmain(int argc, _TCHAR* argv[])
 {
	int m;
	cout << "Enter array size: ";
	cin >> m;
	vector<int> v(m);

	Generator gen;
	generate(v.begin(), v.end(), gen);

	cout << "Content of array: ";
	Printer prn;
	for_each(v.begin(), v.end(), prn);  /**/
	cout << endl;

	int lowerBound = 0, upperBound = 0;
	cout << "Enter the value range: ";
	cin >> lowerBound >> upperBound;

	Predicator prdobj = Predicator(lowerBound, upperBound);
	int result = count_if(v.begin(), v.end(), prdobj);
	cout << "Number of elements in diapason [" << lowerBound << ", " << upperBound << "): ";
	cout  << result << endl;
	cin.ignore(2);
	return EXIT_SUCCESS;
 }

 

Lambda functions

Imagine the situation that a person is unconscious as the result of an electric shock. You test his pulse and check his breathing – that's what is required in this situation. Next, you commence with artificial respiration and an artificial heart massage. All together this is called the pre-medical care and it should be performed by any person which is nearby. I described a critical situation where gender, social status, age, or position does not matter to either the person suffering or who will help. The focus is only on what needs to be immediately done.

Much less dramatic, but rather typical situations in object-oriented programming happen when we are required to determine some type of behavior. For example, when transferring the sorting or filtering method to the STL algorithm. Often it is quite convenient to determine this mode of action right at the place of use. In this case, the function remains unnamed—we do not attach special importance to its name—because we are not going to return to its code again and again.

Here there is a subtle difference between the definition of anonymous functions and lambda expressions. Each lambda is an anonymous function, but not every anonymous function is a lambda. The lambda expression is primarily a syntactic form of writing code, but this does not exhaust the role of the lambda function in the functional paradigm.

Lambda is an entity from the world of functional programming, which illustrates that behavior can be considered in isolation from any other entity. This is even true with its own name, which is closely related to the notion of a pointer. The concept of lambda is closely related to another concept—closure. Thus, lambda uses not only parameters explicitly specified in its signature, but also the context of the calling function (although the nuances of this change from language to language).

 

It’s interesting that the technique of using closures simulates the internal state in functional programming, just as a functor is an imitation of a function in an object-oriented manner.

Syntax

Lambda function – this is an anonymous function that is declared right at the place of the call. In most common cases it looks like:

[ capture-list ] ( params ) mutable(optional) exception attribute -> ret { building }

 

where

capture-list is a comma-separated list of zero or more captures, optionally beginning with a capture-default – one of the symbols ‘=’ (means ‘by value’), ‘&’ (means ‘by reference’); capture-list can contain this token when the pointer of object must be captured too.

Capturing allows you put variables of the outer subroutine in the scope of variables inside your lambda.  

params is a list of params passed into a lambda-function in a traditional way

Empty mask [] means that lambda has no access to outer variables

specifier mutable allows you to modify local values of captures inside lambda itself, but it does not affect the state of variables outside the function  

-> ret shows return type (the syntax is a bit awful, but remember that we are dealing with Latin!)


In addition, you can specify the types of exceptions generated by the lambda using throw syntax.

 

See http://en.cppreference.com/w/cpp/language/lambda for more details, please.

 

In conclusion a small cheat sheet  for those faced with the capture in lambda expressions for the first time – matching access modifiers of variables from the capture scope and analogy for syntax of variables explicitly passed to the function as parameters:

 [x] const x

[x] mutable→ x (by value)

[&x] &x

[&x] mutableseems to me, there is not much sense in this for a single variable in a capture list, but you can check it as an exercise.

Examples

I’ll highlighted two advantages that lambda provides to us in imperative programs. First, lambda expression – this is a laconic description of some simple action, which must be delegated into a foreign method. Second, capture – it is an opportunity for a function that describes such an action, to bypass the imposed restrictions on its signature.

 

Important note: if you use the compilation for the Win32 platform (if you use the C++Builder Starter, then you are using it), you must uncheck the option "Use 'classic' Borland compiler". Turning this compatibility option off lets you use the modern C++ compiler.

 

As an example, let's look at a fairly typical task: we need to initialize an array of integers. We will use std::vector<int> so we can apply a number of algorithms from STL to it later.  

Typically, we have a loop and call of vector::push_back at each iteration.

for( int k = 1; k <= m; k++ )

{

v.push_back(k);

}

 

The container object and its method of adding an element corresponds to an object-oriented paradigm. It contrasts only a simple loop with a call to a specific container method.  What if, for some specific purpose, it allows us to abstract where the next element will be added in the end or the beginning? What if the fact of processing the next element for us is much more important than where exactly this element is added?

 I want to emphasize that this example is too far-fetched, and OOP provides elegant possibilities for the abstraction described above. I also want to hide the details of using of the container in the lambda function call. In this case, using the global function will give us the same effect, but it has a demo purpose only. Nevertheless, capture-list restrictions make using global variables safer.

So, we have a container and its method call:

v.push_back(index);

 

This will be the building of our lambda: this is a simple action that must be performed “here and now.”

Next, at every iteration we must pass an argument to the function; the value of the integer is added to the container. In contrast to the global function, we cannot pass the container object itself as an additional argument, which makes the call at each iteration more concise. Besides, we just want to abstract from the details and concentrate on the changing argument. On the other hand, we can completely control the availability of outer scope variables, thereby avoiding accidental side effects. With capture there is some compromise between brevity and security in regards to the variables passed implicitly.

Initially I try to write this:

op fn = [](int index)  { v.push_back(index); };

 

but this is wrong. You can see the reason in the comments in source code: capture list must be specified in [] and if we ignore it vector v is out of our lambda scope so a compile-time error is raised.

The next attempt

op fn = [v](int index)  { v.push_back(index); };

fails as well: “Candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const,” the compiler says. It happens because we add [v] in capture list, but we still can’t call its methods to modify its state. Adding a new element certainly does it!  

Another issue here that is I’ve define function reference type as

typedef void (*op)(int);

and tried to assign lambda to a variable of this type. This is only the observed signature of our lambda. Taking into account that variables from the capture area are passed implicitly, I’m forced to use the auto keyword  for a function reference type as to bypass this limitation.

Also, I add mutable to the lambda signature, the example code is begins to compile, but it still does not reach the goal because changes occur only locally: this is analogous to the case of parameter passing by value.

The solution is obvious: we will pass the capture area by reference and not by value:

auto fn = [&v](int index) { … };

Our code finally begins to work correctly!

In addition, lambda is used to output the contents of the container in combination with the for_each algorithm. This application is not fictitious: let adherents of pure OOP curse me, but in this context it looks concise and natural. What should we do “for each?” Moreover, iterators look rather ugly in combination with our lambda, but Ranges in C++ that’s quite another story…

#pragma hdrstop
#pragma argsused

#ifdef _WIN32
#include <tchar.h>
#else
  typedef char _TCHAR;
  #define _tmain main
#endif
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

 int _tmain(int argc, _TCHAR* argv[]) 
{
	vector<int> v;
	int m;
	cout << "Enter array size: ";
	cin >> m;

	typedef void (*op)(int);

//	op fn = [](int index)  { v.push_back(index); };
//[bcc32c Error] File1.cpp(24): variable 'v' cannot be implicitly captured in a lambda with no capture-default specified

//	op fn = [v](int index) { v.push_back(index); };
//[bcc32c Error] File1.cpp(27): no matching member function for call to 'push_back'
//  vector(1425): candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const
//  vector(971): candidate function not viable: 'this' argument has type 'const vector<int>', but method is not marked const

	cout << endl;
	cout << "lambda syntax used: " << endl <<" [v](int index) mutable { v.push_back(index); }" << endl;
	auto fn0 = [v](int index) mutable { v.push_back(index); cout << index << " inserted" << endl;  };
	for( int k = 1; k <= m; fn0(k++));

	cout << endl;
	cout << "Received array: ";
	for_each( v.begin(), v.end(), [](int value){ cout<<value<<" ";} );
	cout << "(" << v.size() << " elements)"  << endl;


	cout << "lambda syntax used: " << endl <<" [&v](int index) { v.push_back(index); }" << endl;
	auto fn = [&v](int index) { v.push_back(index);  cout << index << " inserted" << endl; };
	for( int k = 1; k <= m; fn(k++) );
	
	cout << "Received array: ";
	for_each( v.begin(), v.end(), [](int value){ cout<<value<<" ";} );
	cout << "(" << v.size() << " elements)"  << endl;

	cin.ignore(2);
	return EXIT_SUCCESS;
}

Lambdas generated by lambdas

The lambda function is not inferior to the usual function in its capabilities except for the fact that it is anonymous. Therefore, it can return an object of an arbitrary type. But what if we want to return a lambda function (inside the lambda function) to generate a new lambda function and return it as a result?

This is a key opportunity in the framework of the functional paradigm.

It is supported in C++ too, but here we face some difficulties. How should we describe the type of result for the generating lambda function? The result type is the type of our generated lambda function. But what is this type?

Let's recall how we tried to define our lambda function in the first example:

auto fn = [&v](int index) { … };

We came out of the situation by defining the function result type as auto. But we cannot define a signature like it:

[&v](int index)->auto { return [&v](int index) { … };}

It is not difficult to guess why the compiler can not foresee our intent when we specify ‘auto’ as a result type: in the first case, the function already has a full definition and its type is not a surprise for compiler; in the second case, the result type is not known until the return statement is executed, which at the compilation stage can not be uniquely determined.

 

Thus, we require a somewhat different, more flexible mechanism for determining such structures. Ideally, for working in the functional paradigm style, we would like to have a type that would dynamically store functional elements independently from their nature and signature. And indeed, as we shall see later, C++ provides us with such a mechanism.

(to be continued)