If you're looking to partially apply operators without the need for verbose code like:
var gte2 = function (x) { return x >= 2; };
That's a valid use case, focusing on "partially applying operators".
The solution is straightforward. Simply create a curried function. Here's an example:
// var gte = y => x => x >= y; // ES6 syntax
var gte = function (y) {
return function (x) {
return x >= y;
};
};
var gte2 = gte(2);
There are actually two ways to perform partial application with binary operators:
- Partially apply the operator to the left argument.
- Partially apply the operator to the right argument.
This leads us to two key questions:
- Which argument should be defaulted when partially applying the operator?
- How can we partially apply the operator to the other argument?
One thing we can agree on is that providing both arguments to the operator doesn't make sense.
// Instead of writing:
add(2)(3)
// You can simply write:
2 + 3
We create curried operator functions primarily for partial application.
Therefore, providing both arguments to the function simultaneously is counterintuitive.
What does this mean in practice? It implies that:
We have the flexibility to choose any argument order.
// Both options are valid:
var sub = x => y => x - y;
// And:
var sub = y => x => x - y;
The function only needs to make sense with one argument.
// For instance:
var sub = y => x => x - y;
// This works:
sub(1) // interprets as (x => x - 1)
// However, this doesn't work intuitively:
sub(2)(3) // expected (2 - 3) but it calculates (3 - 2)
// Yet, it only needs to make sense given one argument.
So, which argument order is preferable? It all depends.
For commutative operations, argument order is irrelevant.
Both addition and multiplication, for example, are commutative. Hence, a + b = b + a
and a * b = b * a
.
Non-commutative operations typically benefit from a right-to-left argument order as it enhances readability during partial application.
For instance, lt(2)
usually means x => x < 2
, not x => 2 < x
.
Why is this common? In JavaScript, function names precede the argument, so name(arg)
reads naturally as
x => x name arg</code rather than <code>x => arg name x
.
Though there are exceptions to the second guideline. Notably, division:
div(10) // suggests divide 10 by x
// not divide x by 10
Determining the correct argument order for such cases may vary, though left-to-right seems more intuitive to me.
Here are several curried operator functions to consider:
// 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, how do we partially apply these operators to the "other" argument?
The sole approach involves creating a new function with reversed argument orders.
Fortunately, creating new functions for every operator isn't necessary:
For commutative operators, the argument order is interchangeable. Therefore:
flip(add) = add
flip(mul) = mul
Relational operators don't require extra functions either:
flip(lt) = gt
flip(gt) = lt
flip(lte) = gte
flip(gte) = lte
Only flipped operator functions for sub
and div
are essential:
var subFrom = x => y => x - y; // subFrom(5) corresponds to (y => 5 - y)
var divBy = y => x => x / y; // divBy(10) represents (x => x / 10)
In conclusion, trust your intuition when determining the best course of action.