Pure functions (functional programming) - josuamanuel/softwareDevelopmentPrinciples GitHub Wiki
Pure functions (formal approach):
- Given the same input, always return the same output.
- Produce no observable side-effects.
A Pure function is said to acquired the property of referencial transparency. That is, given the same input it always returns the same output.
Using the formal approach functional programming is the composition of pure functions and only pure functions.
For example: Injecting an impure function to be invoked will make the pure function as impure (invoking the function produced side effects).
This is quite a constraint for some languages as javascript that are not pure languages in its core. I recommend a more relax approach to obtain some of the advantages of a pure functional programming.
Pragmatic approach:
- Given equivalent inputs, always return the same output.
- The function does not produced by itself any side-effects. It could rely in a invocation of a function passed as a parameter to produce side effects.
Equivalents --> for example [1] !== [1] but they are equivalent. () => fetch() will never be equivalent to () => fetch() so it still meets the pragmatic approach. Passing this as a parameter will produce different output, but this is expected as fetch() is in a different state each time... the function is pure because the impurity is fence off.
The pragmatic approach lose the referencial transparency. But it still meet Isolation property... it means that it only depends in its input parameter... any side effect is only injected by its paraemter. ISOLATION makes Unit testing feasible. We can stub any impure parameter so the result is totally predictable.
There is a document where you can observe practical functional paradigms in javascript: https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/
Pure examples: x=>x*2
const pi = 3.1416 x=> 2*pi • Is pure because we pi was defined as a constant and it cannot be changed.
let 2pi = (pi=>x=>2*pi)(3.14) • It Is pure because we cannot change the value of the closure pi.
Not Formally pure. Yes pragmatic pure.
(x, f) => { f(x); return x}
• It meets the 1st rule for the same input it will always return the same output. • But, f invocation could cause a side effect. like calling console.log(), or updating a database... • At least this impure function is better than (x) => { f(x); return x} because with the former we can have control of the f (mock or fake) at testing time. the goal in here is not to fight against the nature of javascript and have everything be 100% pure, we want to control when the side effect happens. So in this case the one who controls whether or not to have side effects is the caller of the function. One extra benefit of this is that if you want to use that function in a unit test to prove that it still works as expected the only thing you'll need to do is supply its arguments, you don't have grab any mocking library to test it.
Managing side effects. Example coordinating to API calls.
The goal of functional programming is: • Increase the code coverage with pure functions that implements the business logic. • Strip out from side effects functions any business logic and push it to pure functions. • Impure functions are left only with code that does side effect or the minimum expression to coordinate pure functions (like pipe that is a general meaningless orchestration)
Impure functions:
pipe( createRequestApi1, fetch, DealWithResponseApi1, createRequestApi1, fetch, DealWithResponseApi1 )
fetch
Pure functions:
createRequestApi1, DealWithResponseApi1, createRequestApi1, DealWithResponseApi1
Testing:
To test impure functions we will need to mock fetch. Mocking side effects is complex and costly. The advantage: we can test the whole flow end to end. This is called integration testing.
To test pure functions we just need to put values to the parameters. This is more simple. this is called Unit Testing. It is simple but we cannot test all the pieces together.
First floor:impure.... ground floor: pure
The firstFloor programs deals with the side effect and orchestration: Exceptions, IO, orchestration: pipe, chain, map, await
While the ground floor deals with simple pure function calls.