Functional operations in functional composition

How to stop or expand a composition based on some logical state?

For instance. Suppose I have the following code:

compose( operation4 operation3, operation2, operation1 )(myStuff); 

Or even something like

 myStuff .map(operation1) .map(operation2) .map(operation3) .map(operation4) 

And I want operations 3 and 4 to be executed if myStuff fulfills some conditions.

How to implement this (especially in JavaScript)?

Do I need to create two smaller compositions and have a separate if statement, or is there a way to have a condition inside the composition?

Can fashion solve my problem? If so, how?

+6
source share
4 answers

A simple but practical approach is to have a function like when(cond, f) that executes f (which, in turn, can be a composition) only if cond(x) returns true:

 _do = (...fns) => x => fns.reduce((r, f) => f(r), x); _when = (cond, f) => x => cond(x) ? f(x) : x; // example add3 = x => x + 3; add5 = x => x + 5; add9 = x => x + 9; pipe = _do( add3, add5, _when(x => x > 20, _do(add9)) ) console.log(pipe(30)) console.log(pipe(1)) 
+3
source

To quit prematurely

It is at least possible, but I think this is not a good idea. Functional composition should behave as one function. This is an atomic operation. Therefore, premature exit is not provided out of the box.

Take a step back. The composition of functions n is a classic reduction:

 // generic functions const foldr = f => acc => xs => xs.reduceRight((acc, x, i) => f(x) (acc, i), acc); const comp = f => g => x => f(g(x)); const I = x => x; const inc = x => x + 1; // derived function const compn = foldr(comp) (I); // run console.log( compn([inc, inc, inc, inc]) (0) // 4 ); 

compn performs a similar calculation, for example:

 const I = x => x; const inc = x => x + 1; const computation = x => inc(inc(inc(inc(I(x))))); console.log( computation(0) // 4 ); 

If we want to leave the composition prematurely, we must adapt both the iterative algorithm ( foldr ) and the composition ( compn ):

 // generic functions const foldrk = f => acc => xs => { const next = (i, acc) => i < 0 ? acc : f(xs[i], i) (acc) (acc => next(i - 1, acc)); return next(xs.length - 1, acc); }; const I = x => x; const comp = f => g => x => f(g(x)); const inc = x => x + 1; const lt = y => x => x < y; // derived function compWhile = pred => foldrk(f => acc => k => k( x => pred(x) ? comp(acc) (f) (x) : x )) (I); // run console.log( compWhile(lt(2)) ([inc, inc, inc, inc]) (0) // 2 ); 

The composition continues until the current return value is less than two. The code is hard to understand. I am not even trying to explain it (this is not worth the effort). In any case, compWhile performs the following calculation:

 const comp = f => g => x => f(g(x)); const I = x => x; const inc = x => x + 1; const lt = y => x => x < y; const lt2 = lt(2); const computation = x => lt2(x) ? comp(x => lt2(x) ? comp(x => lt2(x) ? comp(x => lt2(x) ? comp(I) (inc) (x) : x) (inc) (x) : x) (inc) (x) : x) (inc) (x) : x; console.log( computation(0) // 2 ); 

Branching composition

Just divide the composition into smaller compositions that represent the desired branches. If you try to create conditional functions, you will inevitably get unreadable code.

If you have a simple condition, and you do not want to split your composition, at least make the branching as explicit as possible, for example, with a conditional operator :? :

 const foldr = f => acc => xs => xs.reduceRight((acc, x, i) => f(x) (acc, i), acc); const comp = f => g => x => f(g(x)); const I = x => x; const inc = x => x + 1; const compn = foldr(comp) (I); console.log( compn([x => x >= 2 ? x : inc(x), inc, inc]) (0) // 2 ); 

Conclusion

The composition of functions is an atomic operation. Do not try to exit prematurely or branch out inside the composition.

+1
source

Just parse your someObject after each step. An example with a condition callback, but you can implement this check in different ways.

 function compose(...fns) { return function (result, conditionCallback) { for (var i = fns.length - 1; i > -1; i--) { result = fns[i].call(this, result); // Run here condition check if provided. if (conditionCallback && conditionCallback(result) === false) { return result; } } return result; }; }; compose(...functionList)(someObject, function(obj) { return obj.prop !== someValue; }); 

PS: I hope to understand correctly what you meant by โ€œcompositionโ€.

PPS: the example uses ES6 features, but switching to ES5 is very simple.

0
source

I may misinterpret the question, but this is similar to what you can do with arrays or streams, for example, reactive streams or any other similar library.

 var myStuff = "one"; [myStuff] .filter(text => text.length <= 3) .map(text => text.split('').reverse().join('')) .map(text => text.toUpperCase()) .forEach(text => console.log(text)); //prints ENO 

Or using a stream library like RxJS .

 Rx.Observable.of("one","two","three") .filter(text => text.length <= 3) .map(text => text.split('').reverse().join('')) .map(text => text.toUpperCase()) .forEach(text => console.log(text)); //prints ENO, OWT 

And both of the examples above will not go over to reverse and top-level operations if the filter condition is not met, which seems to be what you are asking for.

Although it is so obvious that I feel that your question is probably about something else. In any case, I hope this contributes to the discussion.

-one
source

All Articles