Higher-Order Functions

Higher-Order Functions

This is a crosspost from www.ModernesCpp.com.

Higher-order functions are the pendant to First-Class Functions because higher-order functions can take functions as argument or return them as result. 

Higher-order functions

The three classics: map, filter, and fold

Each programming language supporting programming in the functional style supports at least the three functions map, filter, and fold. map applies a function to each element of its list. filter removes all elements of a list not satisfying a condition. fold is the most powerful of the three ones. fold successively applies a binary operation to pairs of the list and therefore reduces the list to a value.

I can do better:

The name variations

The statement, that each programming language supporting programming in a functional style has to support the three functions map, filter, and reduce, has little restrictions. The names of the three functions have variations in the different programming languages. You can see in the table the names of the Haskell functions in comparison with their names in C++ and Python.

I want to stress two points. Firstly, Haskell has four variations of fold. The variations are due to the fact if the binary operation starts at the begin or at the end of the list and if the binary operation has an initial value. Secondly, string concatenation of map and reduce (fold) gives MapReduce. This is no accident. The Google framework is based on a map and a reduce phase and is therefore based on the ideas of the functions map and reduce (fold).

For simplicity reasons, I will directly display the results in the list syntax of Haskell.

map

map applies a callable to each element of a list. A callable is all that behaves like a function. A callable can be in C++ a function, a function object, or a lambda function. The best fit for higher-order functions is often a lambda function. That for two reasons. At on hand, you can very concisely express your intent and the code is easier to understand. On the other hand, the lambda function defines their functionality exactly at the place where it is used. Because of that, the compiler gets maximum insight in the source code and has the maximum potential to optimise. That is the reason that you will often get more performant executables with a lambda function

Of course, the syntax of lambda functions in Haskell, Python, and C++ is different. Therefore, a lambda function will be introduced in Haskell by a slash \a → a*a, but will be introduced in Haskell by the keyword lambda: lambda x: x*x In C++ you have to use square brackets: [](int i){ return i*i; }. These are only syntactical differences. More interesting is the fact that you invoke functions in Haskell without braces and that Haskell and Python generates a list, while you can modify in C++ the existing std::vector or fill a new one.

filter

filter keeps only this elements in the list that satisfies the predicatTe. A predicate is a callable that returns a boolean. Here is the example.

The function composition isUpper(head x) checks for each word if it starts (head x) with a capital letter (isUpper) ist.

Two quick remarks to std::remove_if. std::remove_if removes no element but it returns the new logical end of the list. Afterwards, you have to apply the erase-remove idiom. The logic of std::remove_if is the other way around. It will remove the elements satisfying the condition. Therefore, I have to negate the condition.

fold

fold is the most powerful of the three higher-order functions. You can implement map and filter by using fold. The code snippet shows the calculation of the faculty of 9 and string concatenation in Haskell, Python, and C++.

foldl needs as the Python pendant reduce and the C++ pendant std::accumulate an initial value. This is in the case of the faculty the 1; this is in the case of the string concatenation the empty string "". Haskell requires two ++ symbols for adding two strings; Python and C++ only one.

The strategy of std::accumulate is a little bit difficult to get. Therefore, I have you a graphic visualising of the processing.

In the first iteration, the initial value 1 together with the first value of the input sequence is used as the arguments for the lambda function: 1*1= 1. The result 1 is the first argument for the lambda function in the second iteration: 1*2= 2. The third iteration has the previous result 2 as the first argument: 2*3= 6. The last iteration returns the final result 24.

What's next?

Pure functional languages such as Haskell have the characteristic that their data is immutable. Therefore, Haskell can not have a for, while, or until loop. In the next post, I will write about the consequences in Haskell.

You should give credit to Joey deVilla (joey@joeydevilla.com). I found it in the net and he gave me permission to use it.

Like
Reply

Loved the explanation with the emoji...! There's something I always wondered, but never had the chance to try, that is, returning a function in C++. Maybe a closure referencing some local scope. That's common practice in Haskell, Python and alike, but I wonder whether the same can be done in C++ and how it works "under the hood".

Like
Reply

To view or add a comment, sign in

More articles by Rainer Grimm

  • Charity run for ALS

    Tomorrow, on the 28th, there will be a charity run for ALS in Rottenburg. The course is exactly 1 km long.

    2 Comments
  • Small Safety Improvements in the C++ 26 Core Language

    Safety is an important concern in C++26. Contracts are probably the most important feature for safety.

    1 Comment
  • My ALS Journey (30/n): Cippi at the CppCon

    This week was very exciting for Cippi. She visited CppCon in Aurora, near Denver.

    2 Comments
  • Contracts: Evaluation Semantic

    After briefly presenting the details of contracts in my last article, “Contracts: A Deep Dive“, I would like to take a…

  • My ALS Journey (29/n): I feel Good

    I often receive messages asking about my health and wishing me well. I am very happy to receive these messages and just…

    5 Comments
  • Contracts: A Deep Dive

    August 25, 2025/in C++26/by Rainer GrimmI already introduced contracts in the article “Contracts in C++26”. In this…

  • My ALS Journey (28/n): Bureaucracy – The German Disease

    Today I want to write about a sad topic. Bureaucracy in the German healthcare system is becoming increasingly absurd.

    2 Comments
  • Data-Parallel Types: Algorithms

    The data-parallel types library has four special algorithms for SIMD vectors. The four special algorithms are min, max,…

  • My ALS Journey (27/n): An Emergency Call

    Firstly, I would like to say that I am doing very well and have made a full recovery from my incident. However, I would…

    8 Comments
  • Data-Parallel Types: Reduction

    In this article, I will discuss reduction and mask reduction for data-parallel types. Reduction A reduction reduces the…

Others also viewed

Explore content categories