8

Function composition composes from right to left:

const comp  = f => g => x => f(g(x));
const inc = x => x + 1;
const dec = x => x - 1;
const sqr = x => x * x;
let seq = comp(dec)(comp(sqr)(inc));

seq(2); // 8

seq(2) is transformed to dec(sqr(inc(2))) and the application order is inc(2)...sqr...dec. Thus the functions are invoked in the reverse order in which they are passed to comp. This isn't intuitive for Javascript programmers, since they're used to method chaining, which goes from left to right:

o = {
  x: 2,
  inc() { return this.x + 1, this },
  dec() { return this.x - 1, this },
  sqr() { return this.x * this.x, this }
}

o.dec().sqr().inc(); // 2

I consider that confusing. Here's a reversed composition:

const flipped = f => g => x => g(f(x));
let seql = flipped(dec)(flipped(sqr)(inc));

seql(2); // 2

Are there any reasons why function composition goes from right to left?

Magne
  • 16,401
  • 10
  • 68
  • 88
  • "*Thus the functions are evaluated in the reverse order of their application*" - huh, what? – Bergi Jun 09 '16 at 14:19
  • You seem to be asking why arguments (right) are evaluated before the function call (left), no? – Bergi Jun 09 '16 at 14:21
  • Would it make more sense to you if `seq` was written `comp(comp(dec)(sqr))(inc)`? (function composition is commutative) – Bergi Jun 09 '16 at 14:25
  • @Bergi I rephrased the sentence in question –  Jun 09 '16 at 14:26
  • @Bergi hm, that wouldn't change the fact, that comp is evaluated from right to left. I haven't any problems with it, I just ask why? –  Jun 09 '16 at 14:30
  • I basically boils down to "function evaluation is always right-to-left". It becomes more obvious if you treat composition as an infix operator. – Bergi Jun 09 '16 at 14:41
  • @Bergi, well that sounds like a dogma. And we can't define our own infix operators in Javascript anyway. –  Jun 09 '16 at 14:53
  • Yes, you can call it dogma, I'd call it convention (stemming from maths): the arguments are always on the right of the function name. You can invert that (like `($ 5) inc` == `inc 5` in Haskell), but that is really just weird and confusing. – Bergi Jun 09 '16 at 14:57
  • @Bergi I'm really bad at math, but isn't mathematical function composition associative that is, `f . (g . h) === (f . g) . h`? Anyway, if it has always been like that and many people are accustomed to it, I don't want to question it. –  Jun 09 '16 at 15:08
  • Oh, yeah I meant associativity of course. You confused me totally by saying that your `compl` function is "left-associative" while you actually commuted (swapped) the arguments. – Bergi Jun 09 '16 at 15:45

2 Answers2

9

To answer the original question: Why does function composition compose from right to left?

  1. So it is traditionally made in mathematics
  2. comp(f)(g)(x) has the same order as f(g(x))
  3. It is trivial to create a reversed or forward composition (see example)

Forward function composition:

const comp = f => g => x => f(g(x));
const flip = f => x => y => f(y)(x);
const flipped = flip(comp);

const inc = a => a + 1;
const sqr = b => b * b;

   comp(sqr)(inc)(2); // 9, since 2 is first put into inc then sqr
flipped(sqr)(inc)(2); // 5, since 2 is first put into sqr then inc

This way of calling functions is called currying, and works like this:

// the original:
comp(sqr)(inc)(2); // 9

// is interpreted by JS as:
( ( ( comp(sqr) ) (inc) ) (2) ); // 9 still (yes, this actually executes!)

// it is even clearer when we separate it into discrete steps:
const compSqr = comp(sqr); // g => x => sqr(g(x))
compSqr(inc)(2);   // 9 still
const compSqrInc = compSqr(inc); // x => sqr(x + 1)
compSqrInc(2);     // 9 still
const compSqrInc2 = compSqrInc(2); // sqr(3)
compSqrInc2;       // 9 still

So functions are composed and interpreted (by the JS interpreter) left to right, while on execution, their values flow through each function from right to left. In short: first outside-in, then inside-out.

But flip has the restriction that a flipped composition can't be combined with itself to form a "higher order composition":

const comp2 = comp(comp)(comp);
const flipped2 = flipped(flipped)(flipped);
const add = x => y => x + y;

   comp2(sqr)(add)(2)(3); // 25
flipped2(sqr)(add)(2)(3); // "x => f(g(x))3" which is nonsense

Conclusion: The right-to-left order is traditional/conventional but not intuitive.

Magne
  • 16,401
  • 10
  • 68
  • 88
3

Your question is actually about the order of arguments in a definition of the function composition operator rather than right- or left-associativity. In mathematics, we usually write "f o g" (equivalent to comp(f)(g) in your definition) to mean the function that takes x and returns f(g(x)). Thus "f o (g o h)" and "(f o g) o h" are equivalent and both mean the function that maps each argument x to f(g(h(x))).

That said, we sometimes write f;g (equivalent to compl(f)(g) in your code) to mean the function which maps x to g(f(x)). Thus, both (f;g);h and f;(g;h) mean the function mapping x to h(g(f(x))).

A reference: https://en.wikipedia.org/wiki/Function_composition#Alternative_notations

FPstudent
  • 831
  • 5
  • 9
  • OK, I confused the commutative with the associative law. Thanks! –  Jun 09 '16 at 17:28
  • 1
    As to alternative notations (";"): we also find "pipe" operators like `>>>` or `|>`, which visualize the "left to right" concept better, IMO. – phipsgabler Jun 10 '16 at 07:40