Higher Order Functions

Cpp

#include <functional>

template<typename T>
T sum(T a, T b) {
    return a + b;
}

template<typename T, typename U>
std::function<U (T)> collect(std::function<U (U, U)> fn, U init) {
    return [fn, init](T list) -> U {
        U out = init;
        for (auto const &l: list) {
            out = fn(out, l);
        }
        return out;
    };
}

int main() {
    auto sumListInt = collect<std::vector<int>, int>(sum<int>, 0);
    auto multListInt = collect<std::vector<int>, int>(
        [](int a, int b) -> int {
            return a * b;
        }, 1);

    sumListInt(std::vector<int> {{1, 2, 3, 4}});
    // returns 10

    multListInt(std::vector<int> {{1, 2, 3, 4}});
    // returns 24
}

JavaScript

function sum(a, b) {
    return a + b;
}

function collect(fn, init) {
    return function(list) {
        let out = init;
        for (var i = 0; i < list.length; i++) {
            out = fn(list[i], out);
        }
        return out;
    }
}

// compose 2 functions to create a new function
const sumList = collect(sum, 0);

// compose with a lambda to create a new function
const multList = collect((a, b) => a * b, 1);

sumList([1, 2, 3, 4])
// returns 10

multList([1, 2, 3, 4])
// returns 24

What This Code Does

The goal of the above code is to combine functions together in order to create new functions. The composability of functions to generate new functions is a form of code-reuse and application of DRY typically found in more functional or function-oriented languages. Our specific goal is to combine collect, which iterates over collection of items, with a function that works on single-elements, to create a new function that works over a collection of elements, to calculate an aggregate value.

What's the Same

The structure of both solutions is identical. While both are idiomatic, both are also very raw. JavaScript has many libraries to help with this, such as Lodash. Similarly, C++ has Boost HOF which contains utilities specifically for HOF programming in C++.

What's Different

The differences mostly lie in type declarations, syntax, and the necessity to use generic-like programming in order to work for multiple types (int, float, etc). Most of these differences are covered in the Lambdas versus.

This is actually a really good thing! C++, being a language incredibly focused on performance and low-level details, can still make use of higher-level language features one would expect to find in modern programming languages.

Partial Application

Cpp

#include <boost/hof.hpp>
#include <functional>

template<typename T>
T sum(T a, T b) {
    return a + b;
}

int main() {
    std::function<int(int)> addFiveInt = boost::hof::partial(
            BOOST_HOF_LIFT(sum)
        )(5);

    addFiveInt(10);
    // returns 15
}

JavaScript

const _ = require('lodash');

function sum(a, b) {
    return a + b;
}

const addFive = _.partial(sum, 5);

addFive(10);
// returns 15

What This Code Does

Partial application is the ability to create a new function by only partially applying parameters to the original function.

In the above code we create a new function from sum(a, b) by applying 5 as the first parameter. This new function addFive is the same as calling sum(a, b) where a is always 5.

What's the Same

Both the Javascript and the C++ solutions make use of 3rd party libraries and both create a new function by passing the sum and the value 5 into the 3rd party's partial function.

What's Different

Again, the main differences here are the types (see Lambdas) and the libraries used. You may note one small difference that an additional utility is used in the C++ code that is not present in the JavaScript version, BOOST_HOF_LIFT. This is a simple utility that allows us to use partial on generic (templated) functions.

Function Currying

Cpp

#include <iostream>
#include <boost/hof.hpp>
#include <functional>
#include <vector>

using namespace std;
namespace hof = boost::hof;

template<typename T>
vector<T> concat(const vector<T>& a, const vector<T>& b) {
	vector<T> ret(a.cbegin(), a.cend());
	ret.insert(ret.end(), b.cbegin(), b.cend());
	return ret;
}

// --- Example with vanilla Currying ---
auto curry2 = [](auto func) {
	return [=](auto x) {
		return [=](auto y) {
			return func(x, y);
		};
	};
};

int main()
{
	auto concatCurried = curry2(concat<int>);
	auto prepend012 = concatCurried(vector<int>{0, 1, 2});

	prepend012(vector<int>{7, 8, 9});
	// returns  [0, 1, 2, 7, 8, 9]

	// --- Example with Auto-Currying ---
	auto sum = [](auto a, auto b){
		return a + b;
	};

	auto biOp = [](auto func, auto a, auto b) {
		return func(a, b);
	};

	auto biOpCurried = hof::partial(biOp);
	auto plus = biOpCurried(sum);
	auto addFivePFive = plus(5.5);

	addFivePFive(4.5);
	// returns 10
}

JavaScript

const concat = function(a, b){
	return a.concat(b);
};

// --- Example with vanilla Currying ---
const curry2 = function(func){
	return function(x){
		return function(y){
			return func(x, y);
		};
	};
};

const concatCurried = curry2(concat);
const prepend012 = concatCurried([0, 1, 2]);

prepend012([7, 8, 9]);
// returns [0, 1, 2, 7, 8, 9]

// --- Example with Auto-Currying ---
const _ = require('lodash');

const sum = function(a, b) {
	return a + b;
};
	
const biOp = function(a, b, c) {
	return a(b + c);
};

// use auto-currying for functions with fixed arity of func.length
const biOpCurried = _.curry(biOp);
const plus = biOpCurried(sum);
const addFivePFive = plus(5.5);
	
addFivePFive(4.5);
// returns 10

What This Code Does

Function Currying is the creation of n functions with a single argument from a single function with n arguments. Calling a curried function with an argument is like partially applying that argument. Partial Application and Currying are very similar, however Currying a function is done without providing any argument to the original function.

In the example of vanilla currying, curry is given for binary functions. It is used to curry a binary concat function for arrays. By calling the curried function with an array of integers from 0 to 2 we arrive at a new function which prepends these values to it's argument (i.e. the second parameter of the concat function).

In the second example with auto-currying we take a binary function invoker biOp with three arguments and create a new curried function by using a utility function. By calling the curried function in turn with the separate arguments of the original function we receive more and more specialised (and still curried) functions: First a binary sum function plus and last a function which just adds 5.5.

What's the Same

Both the Javascript and the C++ solutions make use of 3rd party libraries (Lodash and HOF respectively) to create a curried function from biOp. (Hint: For Functional Programming in JavaScript other libraries like Ramda maybe more suited.)

What's Different

Type deduction with auto

In order to remove syntactic clutter from the C++ solution, the templated functions are replaced by generic lambdas with auto-typed parameters and return values. auto lets the compiler deduce the type of a variable based upon the types of initialisation and return values. Think of

template<typename X, typename Y>
Y some_function(X x){ // return some result Y };
as
auto some_function(auto x){ /*return some result Y */ };
It makes the code easier to read and more resilient to changes. For instance in the second example the compiler infers the types of a and b from 5.5 and 4.5 respectively. Better explanations can be found elsewhere.

When using Lambdas, the code to define the vanilla versions of the curry functions curry is nearly identical. Also the code invoking the curried functions is not much different.

Fork me on GitHub