# FP in C++: Function composition

## Combining functions enables creating complex functionality

### Output becomes input

Output of the inner function becomes the input of the outer function.

### Type match is essential

Composition requires output-to-input type match.

g:: double -> double
f::           double -> bool

### Not all functions can be composed

Function f expects a single value, however g returns an array.

g:: int -> [int]
f::         int -> bool

This problem can be solved with monads and Kleisli composition.

### Q: What is the result of the composition f(g(x))?

f(x) = x2 - x - 1
g(x) = x + 1

Hint:
Substitute x in f with the definition of g.

### Use nested lambda to express composition

auto compose = [](auto f, auto g) {
return [=](auto x) {
return f(g(x));
};
};
auto op = compose(f2, f1);

Unary function composition only.

## Let's look at a concrete example

### We have the following functions at hand

double fahrenheitToCelsius(double fahr){
return (fahr - 32) * 5.0 / 9.0;
}
bool isRoomTemperature(double t){
return (t >= 20.0 && t < 25.0) ? true : false;
}

### All we need to do is compose functions

Notice the order, we compose from right to left.

auto op = compose(isRoomTemperature, fahrenheitToCelsius);
auto val1 = op(71.9); // returns 1, i.e. room temperature
auto val2 = op(50.3); // returns 0, i.e. not room temperature

### We could have created a distinct function for this purpose

auto opComposed(double fahr){
return isRoomTemperature(fahreheitToCelsius(fahr));
}
int main() {
auto val = opComposed(71.9); // room temperature
}

### However, composition gives us elegance and generality

auto vecBool = map(vecTemp,
compose(isRoomTemperature, fahrenheitToCelsius));

The composed function can be directly fed into higher-order functions, such as map and filter.

### Q: Compose f(g(x)), then g(f(x))

f(x) = 0.5x - 5
g(x) = 2x + 10

Hint:
Try a different function pair.

## Did you get tricked by the previous question?

### Composition is not commutative in general

f • g ≠ g • f

The result happened to be the same, because the functions were mutual inverses.

### We have the following functions at hand

double bmi(double weight, double height){
return weight / height / height;
}
bool isOverweight(double bmi){
return (bmi >= 25.0) ? true : false;
}

## We need to supply 2 values to calculate BMI

### Multi-argument composition requires parameter pack

auto compose = [](auto f, auto g) {
return [=](auto&& ... x) {
return f(g(x...));
};
};

Three dots ... in C++ denotes the parameter pack.

### Passing multiple parameters becomes simple

auto op = compose(isOverweight, bmi);
auto val1 = op(59.8, 1.7); // returns 0, i.e. false
auto val2 = op(73.0, 1.52); // returns 1, i.e. true

### Composition is always associative

(f • g) • h = f • (g • h) compose(compose(f,g),h)=compose(f,compose(g,h))

### Q: What is the result of the composition

(f • g) • h and f • (g • h)?

f(x) = x - 2, g(x) = x2, h(x) = 2x,

## Distinguish between function composition and application

### Function application requires that we supply concrete values for one or more arguments.

Full application produces a value, partial application produces a function that "remembers" values.

### Q: Mark all cases of full application

Hint:
Were concrete values supplied?

## Let's sum it all up

Function composition

• produces a more complicated function
• requires output-to-input type match
• its result is another function
• is associative but generally not commutative
• is expressed via nested lambda expressions
• multi-argument composition in C++ requires the use of the parameter pack