user3016883
user3016883

Reputation: 318

To "combine" functions in javascript in a functional way?

I'm learning functional programming and I wonder if there is a way to "combine" functions like this:

function triple(x) {
    return x * 3;
}
function plusOne(x) {
    return x + 1;
}
function isZero(x) {
    return x === 0;
}
combine(1); //1
combine(triple)(triple)(plusOne)(1); // 10
combine(plusOne)(triple)(isZero)(-1); // true

If the para is a function, it "combines" the function into itself, and if not it will return the final result. Thanks!

Upvotes: 6

Views: 13544

Answers (5)

Mulan
Mulan

Reputation: 135197

heritage

This is a concept from maths called function composition.

       f(x) = y
       g(y) = z

    g(f(x)) = z

   (g•f)(x) = z

That last line is read "g of f of x equals z". What's great about composed functions is the elimination of points. Notice in g(f(x)) = z we take an x input and get a z output. This skips the intermediate point, y.

Composition is a great way to create higher-order functions and keep your code sparkly clean. It's plain to see why we'd want this in our Javascript programs.


comp

JavaScript is a multi-paradigm language with rich support for functions. We can create a simple comp function, which combines two input functions, g and f, and results in a new function -

function triple(x) {
  return x * 3
}

function plusOne(x) {
  return x + 1
}

function comp(g, f) {
  return function(x) {
    return g(f(x))        // "g of f of x"
  }
}

const myfunc =
  comp(triple, plusOne)

console.log(myfunc(1))

Evaluation

triple(plusOne(1))
triple(2)
6

compose

Just as the question suggests, it's likely we will want to combine more than two functions. Below we write compose which takes all of the input functions and reduces them using our simple comp from above. If no functions are given, we return the empty function, identity -

const triple = (x) =>
  x * 3

const plusOne = (x) =>
  x + 1

const comp = (g, f) =>
  x => g(f(x))                     // "g of f of x"

const identity = (x) =>
  x

const compose = (...all) =>
  all.reduce(comp, identity)

const myfunc =
  compose(triple, triple, plusOne) // any amount of funcs

console.log(myfunc(1))

Evaluation

triple(triple(plusOne(1)))
triple(triple(2))
triple(6)
18

pipe

You can be as creative as you like. Below, we write pipe which allows our programs to read in a comfortable left-to-right direction -

const triple = (x) =>
  x * 3

const plusOne = (x) =>
  x + 1

const pipe = x =>
  f => pipe(f(x))

pipe(1)(plusOne)(triple)(triple)(console.log)           // 18
pipe(3)(triple)(plusOne)(triple)(plusOne)(console.log)  // 31

Evaluation of expression one -

f => pipe(f(1))
pipe(plusOne(1))
f => pipe(f(2))
pipe(triple(2))
f => pipe(f(6))
pipe(triple(6))
f => pipe(f(18))
pipe(console.log(18))
18

and expression two -

f => pipe(f(3))
pipe(triple(3))
f => pipe(f(9))
pipe(plusOne(9))
f => pipe(f(10))
pipe(triple(10))
f => pipe(f(30))
pipe(plusOne(31))
f => pipe(f(31))
pipe(console.log(31))
31

related techniques

Curried functions and partial application are concepts that gel with function composition. pipe above is introduced in another Q&A as $ and demonstrated again here -

const $ = x =>           // "pipe", or whatever name you pick
  k => $ (k (x))
  
const add = x => y =>    // curried add
  x + y

const mult = x => y =>   // curried mult
  x * y
  
$ (1)                    // 1
  (add (2))              // + 2 = 3
  (mult (6))             // * 6 = 18
  (console.log)          // 18
  
$ (7)                    // 7
  (add (1))              // + 1 = 8
  (mult (8))             // * 8 = 64
  (mult (2))             // * 2 = 128
  (mult (2))             // * 2 = 256
  (console.log)          // 256

Upvotes: 42

Hitmands
Hitmands

Reputation: 14159

Function composition has already been detailed in other answers, mostly https://stackoverflow.com/a/30198265/4099454, so my 2 cents are straight onto answering your latest question:

If the para is a function, it "combines" the function into itself, and if not it will return the final result. Thanks!

const chain = (g, f = x => x) => 
  typeof g === 'function'
  ? (y) => chain(y, (x) => g(f(x)))
  : f(g);

// ====

const triple = x => x * 3;
const inc = x => x + 1;
const isZero = x => x === 0;

console.log(
  chain(inc)(triple)(isZero)(-1),
);

Upvotes: 1

Mamunur Rashid
Mamunur Rashid

Reputation: 1185

It is also possible to build a complex Functionality by Composing Simple Functions in JavaScript.In a sense, the composition is the nesting of functions, passing the result of one in as the input into the next. But rather than creating an indecipherable amount of nesting, we'll create a higher-order function, compose(), that takes all of the functions we want to combine, and returns us a new function to use in our app.

function triple(x) {
  return x * 3;
}
function plusOne(x) {
  return x + 1;
}
function isZero(x) {
  return x === 0;
}

const compose = (...fns) => x =>
  fns.reduce((acc, cur) => {
    return cur(acc);
  }, x);

const withCompose = compose(triple, triple, isZero);
console.log(withCompose(1));

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386520

function triple(x) {
    return x * 3;
}
function plusOne(x) {
    return x + 1;
}
function isZero(x) {
    return x === 0;
}

var combine = function (v) {
    var fn = [];
    function _f(v) {
        if (typeof v === 'function') {
            fn.push(v);
            return _f;
        } else {
            return fn.reduce(function (x, f) { return f(x); }, v);
        }
    }
    return _f(v);
};

var a, b;
console.log(combine(1)); //1
console.log(combine(triple)(triple)(plusOne)(1)); // 10
console.log(combine(plusOne)(triple)(isZero)(-1)); // true
console.log(a = combine(plusOne)); // function ...
console.log(b = a(triple)); // function ...
console.log(b(5)); // 18
console.log(combine(triple)(plusOne)(triple)(plusOne)(triple)(plusOne)(1)); // 40
// @naomik's examples
var f = combine(triple); 
var g = combine(triple)(triple); 
console.log(f(1)); // 3
console.log(g(1)); // 9 (not 6 as you stated)

Upvotes: 1

Spencer Wieczorek
Spencer Wieczorek

Reputation: 21565

You can simply call the function on the return values themselves, for example:

plusOne(triple(triple(1))) // 10
isZero(triple(plusOne(-1))) // true

Upvotes: -1

Related Questions