1

Given is a simple, mathematically curried function to subtract numbers:

function sub(x) {
  return function (y) {
    return x - y;
  };
};

sub(3)(2); // 1

The function signature reads exactly as the obtained result. The situation changes as soon as function composition is involved:

function comp(f) {
  return function (g) {
    return function (x) {
      return f(g(x));
    };
  };
};

function gte(x) {
  return function (y) {
    return x >= y;
  };
};

comp(gte(2))(sub(3)) (4); // true

With function composition, the last parameter of each function involved is crucial, as it is fed respectively with the value returned by the previously applied function. The composition in the above example reads therefore as: 4 - 3 >= 2 which would yield false. In fact, the computation behind the composition is: 2 >= 3 - 4, which yields true.

I can rewrite sub and gte easily to get the desired result:

function sub(y) {
  return function (x) {
    return x - y;
  };
};

function gte(y) {
  return function (x) {
    return x >= y;
  };
};

comp(gte(2))(sub(3)) (4); // false

But now the return values of directly called functions are different than expected:

sub(3)(2); // -1 (but reads like 1)
gte(2)(3); // true (but reads like false)

I could switch the arguments for each call or define a partial applied function for each case:

function flip(f) {
  return function (x) {
    return function (y) {
      return f(y)(x);
    };
  };
}

flip(gte)(2)(3); // false

var gteFlipped = flip(gte);
gteFlipped(2)(3); // false

Both variants are obviously cumbersome and neither more readable.

Which parameter order is preferable? Or is there a mechanism to use both, depending on the respective requirements (like Haskell's left/right sections for partially applied operators)?

A possible solution must take into account, that I use unary functions only!

  • Perhaps you are looking for this: https://github.com/algesten/fnuc – Aadit M Shah Jan 05 '16 at 16:17
  • @AaditMShah: I know this question, but it refers to normal, n-ary Javascript functions with an external curry function. –  Jan 05 '16 at 16:27
  • If you feel like you question is not answered then you can vote to reopen the question. Until then, I'll think about your problem and try to find a solution for it. – Aadit M Shah Jan 05 '16 at 16:49

3 Answers3

1

So you want to partially apply operators without having to write code like:

var gte2 = function (x) { return x >= 2; };

That's a reasonable use case, “to partially apply operators”.

The answer is simple. Just write a curried function. For example:

// var gte = y => x => x >= y; // ES6 syntax

var gte = function (y) {
    return function (x) {
        return x >= y;
    };
};

var gte2 = gte(2);

Unfortunately, there are two ways we can partially apply binary operators:

  1. Partially apply the operator to the left argument.
  2. Partially apply the operator to the right argument.

This raises two important questions:

  1. Which argument should the operator be partially applied to by default?
  2. How do we partially apply the operator to the other argument?

Fortunately, we can agree on one thing: it makes no sense to provide both arguments to the operator.

// Why write the following:

add(2)(3)

// When you can write the following:

2 + 3

The reason we are creating curried operator functions in the first place is for partial application.

Hence, it makes no sense to provide both arguments to the function “at once”.

What are the ramifications of this restriction? It means that:

  1. We can choose any argument order we want.

    // This is correct:
    
    var sub = x => y => x - y;
    
    // So is this:
    
    var sub = y => x => x - y;
    
  2. It only needs to make sense given one argument.

    // Consider this operator function:
    
    var sub = y => x => x - y;
    
    // This makes sense:
    
    sub(1) // means (x => x - 1)
    
    // However, this doesn't make sense:
    
    sub(2)(3) // expected (2 - 3) but actually (3 - 2)
    
    // But that's OK because it only needs to make sense given one argument.
    

Now, keeping this in mind which is the best argument order? Well, it depends.

  1. For commutative operations the argument order doesn't matter.

    For example, both addition and multiplication are commutative. Hence, a + b = b + a for addition and a * b = b * a for multiplication.

  2. For non-commutative operations the right-to-left argument order is generally better because it allows the partial application to be read out loud.

    For example, the expression lt(2) generally means x => x < 2 and not x => 2 < x.

    Why is this the general case? Well, in JavaScript function names come before the argument. Hence, name(arg) naturally reads as x => x name arg instead of x => arg name x.

  3. However, there are some exceptions to the second rule. Most prominently, division:

    div(10) // is read out loud as divide 10 by x
            // it is not read out loud as divide x by 10
    

    Of course, the correct argument order for such edge cases is a matter of debate but in my humble opinion the left-to-right argument order seems more natural.

So, here are a bunch of curried operator functions:

// Commutative operators:

var add = x => y => x + y;
var mul = x => y => x * y;

// Right-to-left operators:

var lt  = y => x => x < y;
var gt  = y => x => x > y;
var lte = y => x => x <= y;
var gte = y => x => x >= y;
var sub = y => x => x - y;

// Left-to-right operators:

var div = x => y => x / y;

Now, the second question is how do we partially apply the operators to “other” argument?

The only way to do so it to create a new function with the flipped argument order.

Fortunately, we don't need to create a new function for every operator:

  1. For commutative operators the argument order doesn't matter. Hence:

    flip(add) = add
    flip(mul) = mul
    
  2. For relational operators we don't need to create extra functions:

    flip(lt)  = gt
    flip(gt)  = lt
    flip(lte) = gte
    flip(gte) = lte
    
  3. We only need to create flipped operator functions for sub and div:

    var subFrom = x => y => x - y; // subFrom(5) means (y => 5 - y)
    var divBy   = y => x => x / y; // divBy(10) means (x => x / 10)
    

All things considered, I would say that you should use your common sense.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • I do read `div(10)` as "divide by 10". I also read `mult(3)` as "multiply by 3". I see you eventually warmed up to ES6 syntax too. – Mulan Jan 06 '16 at 08:18
  • Hey @naomik. Yes, quite a lot of people read `div(10)` as "divide by 10" instead of "divide 10 by" which is why I added that "the correct argument order for such edge cases is a matter of debate". The main reason why I chose the name `div` for `x => y => x / y` is because of the lack of a better name. I was considering naming it `divOver` so that my naming convention would be consistent with `subFrom` but that seems to be more confusing than intuitive. Yes, ES6 syntax is much better than conventional JS syntax. However, I'll refrain from using it until it's more widely supported by browsers. – Aadit M Shah Jan 06 '16 at 15:06
  • I guess you really meant `not(lte) === gt` due to `flip(lte) === gte`. –  Jan 09 '16 at 08:08
0

Here's how I read your composition:

comp(gte(2))(sub(3)) (4);

gte(2) = function(y) { return 2 >= y; } // (x = 2)
sub(3) = function(y) { return 3 - y; } // (x = 3)

// Therefore:
comp(gte(2))(sub(3)) = function(x) {
    var f = function(y) { return 2 >= y; };
    var g = function(y) { return 3 - y; };
    return f(g(x));
};

// Now call with (x = 4):
x = 4
g(4) = 3 - 4 = -1
f(-1) = (2 >= -1) = true

So in short, it seems that your expectations are wrong. Maybe you do indeed have something backwards, but I honestly can't tell what. I do, however, think that this is not a good way to work in JavaScript and you're overcomplicating things, but that's just my opinion.

Niet the Dark Absol
  • 320,036
  • 81
  • 464
  • 592
  • So you mean that my misunderstanding is already in my assertion, that `comp(gte(2))(sub(3)) (4);` reads as `4 - 3 >= 2`? –  Jan 05 '16 at 16:43
  • Yup. It looks like it ends up being `2 >= 3 - 4` which, you will notice, has your operands in order as well as the functions called. – Niet the Dark Absol Jan 05 '16 at 16:51
  • I am aware that this style is unusual for Javascript, but I am convinced it is the most accurate, if you prefer functional programming. –  Jan 05 '16 at 18:11
0

This response is based on the reply from Aadit.

There is actually a need for fully applied curried operator functions in Javascript - when used as First Class Citizens:

function between(ops) {
  return function (left) {
    return function (right) {
      return function (n) {
        // At this point one should use the native Javascript operators
        // but they can't be passed to the function, since operators are not First Class.
        return ops[0](left)(n) && ops[1](right)(n);
      };
    };
  };
}

function lte(y) { return function (x) { return x <= y; }; }
function gt(y) { return function (x) { return x > y; }; }

between([gt, lte])(2)(4)(4); // true
// is evaluated as: gt(2)(4) && lte(4)(4) === true; (confusing)

between may be nonsense, but it serves as proof that fully applied curried operator functions make sense in Javascript. There are likely even other use cases.

Nevertheless Aadit is right that something like sub(2)(3) contradicts the very purpose of currying!

So how could a solution look like?

  1. all curried operator functions must have a right-to-left argument order
  2. a function is introduced, which explicitly indicates the unusual usage when passing all arguments to a curried function at once

Here is uncurryOp:

// intended for all operator functions
function uncurryOp(f) {
  return function (x, y) {
    return f(y)(x);
  };
}

uncurryOp(gt)(2, 4); // false (intuitive)

This is not really an adequate solution. I think there is no, due to lack of First Class and partially applicable operators in Javascript.

  • Your `uncurryOp` function is the same as `flip`. – Aadit M Shah Jan 09 '16 at 16:20
  • @AaditMShah Not exactly. `uncurryOp` is like `flip` combined with `uncurry`. And I think `uncurryOp(gt)(2, 4)` is more readable than `flip(gt)(2)(4)`. But that's just my opinion. –  Jan 09 '16 at 16:37
  • I've written an autocurried `flip` function which allows you to write `flip(gt, 2, 4)`. See the following answer: http://stackoverflow.com/a/27996545/783743 – Aadit M Shah Jan 09 '16 at 16:40