Question

I understand what pure functions are and when someone says pure functions are composable - I believe it means that the output of one function can be passed as an input to another function but same thing goes with impure functions as well, doesn't it? Take the two functions below:

int sum(int a, int b) {
    print("sum");
    return a + b;
}

void save(int result) {
    DB.save(result);
}

void PersistSum(int a, int b) {
    save(sum(a,b));
}

Although the save and sum functions have side effects - they can still be composed as in PersistSum

I am sure am missing some core meaning of composability here. Can someone provide a precise meaning of what composability means and why functions having side effects are non-composable with an example?

Was it helpful?

Solution

Function composition is indeed creating a new function that applies one function on the output of another one. More generally, composition allows to create a new function by combining several other functions.

Pure functions are functions that always provide the same output for the same input and have no side effects. So a function without any surprise, that you can call how often you may need to since only its result matters.

You can compose pure functions. You will get, by definition, a pure function.

You can also compose impure functions. It's just that it's more risky.

Take an example with h(x) = g(f(x)):

  • If f and g have two totally independent side effects, sf and sg, these side effects will happen in the following order: first sf, then sg.
  • If the side effects are not independent, then composition can have unexpected effects.

Suppose now that you have a more complex composition, using several variables and more than two functions: h(x,y) = g(f1(x,y),f2(x,y)). As already said, if all these functions are pure the result will be pure without surprise. If these functions are impure a lot of open questions arise:

  • Suppose each of these function has independent side effects sg, sf1, sf2. Here you no longer can predict the order of these side effects: it could be sf1, sf2, sg. It could as well be sf2 sf1 sg or even sf1 and sf2 exactly in the same time and then sg.
  • Suppose that these side effects are not independent, for example that sf1 adds 1 to a global variable and sf2 multiplies by 2 the same global variables, the effect of h on the global variable would be unpredictable.

OTHER TIPS

Expressions with pure functions can be rewritten, and then any side effects happen differently.

f x = { print(x), x }
^ (xor) is commutative an associative, x ^ 0 == x, x ^ x == 0

This can be used to filter out duplicates, by resorting the terms:

f 3 ^ f 5 ^ f 7 ^ f 5 ^ f 3 ==
(f 3 ^ f 3) ^ (f 5 ^ f 5) ^ f 7  ==
0 ^ 0 ^ f 7 ==
f 7 ==                     -- Just one print(7)
7

So side effects can happen in different orders or not at all, if they are ignored.

My point: function composition also concerns expression tree operations.

(Also mind that there is no guarantee that two calls with same arguments will give the same result. But that aspect you did not intend.)

Licensed under: CC-BY-SA with attribution
scroll top