Pragmatic Purity
Keep pure and impure code separate, with fewer impure code paths
The code below follows a common pattern in impure code.
if (condition) {
doFooImpurely();
} else {
doBarImpurely();
}
If you can extract out common logic from doFooImpurely
and doBarImpurely
into a meaningful intermediate variable that can be calculated purely, so the code is of the following form
const intermediateValue = condition ? getFooValuePurely() : getBarValuePurely();
doImpurely(intermediateValue);
then I would recommend considering it. Even if the total cycomatic complexity of the code is equal to what it was, I usually judge the latter to be better because
- the pure and impure parts of the code are separate, and so better due to the standard separation of concerns arguments;
- depending on what is inside
doImpurely
, there are likely to be fewer impure code paths: this is often better since there are fewer ways state can change or side effects can happen.
The benefit is greater in more complex cases.
if (condition) {
doFooImpurely();
} else if (anotherCondition) {
doBarImpurely();
} else {
doQuxImpurely();
}
If you can extract out the pure logic, the code can then take the below form.
const intermediateValue =
condition ? getFooValuePurely() :
(anotherCondition ? getQuxValuePurely() : getBarValuePurely());
doImpurely(intermediateValue);
If you're not a fan of the nested ternary operator, you can easily restructure the pure part of the code: make it a series of if statements, extract out to another function, etc. A really nice benefit of pure code is that you can really easily move code about, and have confidence that it doesn't negatively affect the code that "does stuff", i.e. the impure code.