Repository: sanctuary-js/sanctuary
Pull request #391 (2017-05-11 05:10:49)

number: flip S.sub argument order, and remove S.dec and S.inc
__Problem:__ Defining _subtract N_ functions is awkward. There are currently two options:

```javascript
> S.map(S.sub(S.__, 1), [1, 2, 3])
[0, 1, 2]

> S.map(S.add(-1), [1, 2, 3])
[0, 1, 2]
```

Furthermore, the intuitive approach is incorrect:

```javascript
> S.map(S.sub(1), [1, 2, 3])
[0, -1, -2]
```

__Solution:__ Flip `S.sub` argument order. The intuitive approach then works as expected:

```javascript
> S.map(S.sub(1), [1, 2, 3])
[0, 1, 2]
```

__Problem with the solution:__ It's surprising for `S.sub(7, 2)` to evaluate to `-5` rather than `5`.

__Solution to the problem with the solution:__ Make `S.sub` *unary*. `S.sub(7, 2)` is then a type error:

```javascript
S.sub(7, 2);
// ! TypeError: Function applied to too many arguments
//
//   sub :: FiniteNumber -> (FiniteNumber -> FiniteNumber)
//
//   ‘sub’ expected at most one argument but received two arguments.
```

One can use `S.sub_` in situations in which a binary subtraction function is required.

---

This pull request also removes `S.dec` and `S.inc`. `S.dec` was included in the library in large part because `S.sub(S.__, 1)` and `S.add(-1)` are awkward. `S.inc` was included for symmetry. In a world in which `S.sub(1)` defines the _subtract 1_ function, `S.dec` does not warrant inclusion, in my view. Do others agree?

/cc @CrossEye, @buzzdecafe


buzzdecafe: You rang?

The broader problem is: Is there a way in JS to handle non-commutative infix operators converted to prefix _that preserves readable semantics_? IMO that answer is "no", having been on this snipe hunt several times. 

Given that my answer to the above is "no", and that the new JS lambda syntax is so compact, my recommendation is not to have infix-to-prefix functions on the API at all. Use a lambda. I'd be happy to see such functions removed from the ramda API. 

Sorry if this $0.02 is not much help. 
davidchambers: > Given […] that the new JS lambda syntax is so compact, my recommendation is not to have infix-to-prefix functions on the API at all. Use a lambda.

A lambda works fine for `S.sub(1)`, which can easily be rewritten `x => x - 1`.

A lambda does not work for `S.gte(S.Just(0))`, which is not equivalent to `x => x >= S.Just(0)` due to [Ord][1].

We have no choice but to define `S.gte` in #388. The only questions are arity and argument order.


[1]: https://github.com/fantasyland/fantasy-land#ord

gabejohnson: Another option I've been kicking around this morning is to have an `infix` function (not necessarily by that name).
```js
S.lt(2)(1) // true
S.lt(2, 1) // true
S.infix(2, S.lt, 1) // false
```
You could even curry it to give the semantics of the current batch of binary functions we've discussed.
```js
S.infix(2, S.lt)(1) // false
// or potentially
S.sectionl(2, S.lt)(1) // false
S.sectionr(2, S.lt)(1) // true
```
`sectionl` and `sectionr` would have the semantics described [here](https://wiki.haskell.org/Section_of_an_infix_operator).
buzzdecafe: > A lambda does not work for `S.gte(S.Just(0))`

I thought that might run into difficulty deeper into the `S` weeds. I like the idea of `infix` or `section*` functions to make the intent crystal clear. But ultimately you won't get anything as nice as `(^ 2)` vs `( 2 ^)`

gabejohnson: `l_(2, lt)` and `r_(lt, 2)`?
davidchambers: > I thought that might run into difficulty deeper into the `S` weeds.

What did you mean by this?

> ultimately you won't get anything as nice as `(^ 2)` vs `(2 ^)`

I started working on a new programming language a few days ago. It will bring together the things I most appreciate about Haskell (curried functions) and Lisp (uniform syntax). Here's a teaser:

```haskell
> S.map (- 1) [1, 2, 3]
[0, 1, 2]

> S.filter (S.gte (S.Just 0)) [S.Nothing, S.Just -1, S.Just 0, S.Just 1]
[Just 0, Just 1]
```

This may be possible with the help of a macro:

```haskell
> S.filter (>= S.Just 0) [S.Nothing, S.Just -1, S.Just 0, S.Just 1]
[Just 0, Just 1]
```

> Another option I've been kicking around this morning is to have an `infix` function (not necessarily by that name).

Interesting idea, Gabe. `S.infix(2, S.lt, 1)` reads well to me, but I have difficulty interpreting the partially applied forms.

gabejohnson: @davidchambers, you could instead have a family of three related functions
```js
S.sub(3, 2) // 1
// infix
S.in_(3, sub, 2) // 1
// left section
S.l_(3, sub)(2) // 1
// right section
S.r_(sub, 3)(2) // -1
```
The reason I chose `in_` instead of `i_` is to contrast it with `l_`. They would be difficult to distinguish from each other visually.

The benefit of this approach is that you don't need any special cases for associative binary functions. They can just be vanilla, uncurried functions.
buzzdecafe: > > I thought that might run into difficulty deeper into the S weeds.
> What did you mean by this?

That simple lambdas would run afoul of the type system in more constrained contexts.

davidchambers: > The benefit of this approach is that you don't need any special cases for associative binary functions. They can just be vanilla, uncurried functions.

That's nice! I like the switch you made to the `r_` argument order. `r_(lt, 2)` reads well to me. The problem remains that the most intuitive expression, `lt(2)`, does not behave as one might expect.

> That simple lambdas would run afoul of the type system in more constrained contexts.

I believe you missed my intended point, @buzzdecafe, which does not concern type checking. `Z.lte(S.Just(0), S.Just(1))` is equivalent to `S.Just(0)['fantasy-land/lte'](S.Just(1))`. Without `valueOf` shenanigans we can't have `<=` dispatch to `fantasy-land/lte`.

gabejohnson: > The problem remains that the most intuitive expression, `lt(2)`, does not behave as one might expect.

My thinking is that `lt(2)` would throw an arity error. The only way to partially apply would be to use either `curry2` or one of the section functions.
davidchambers: > My thinking is that `lt(2)` would throw an arity error. The only way to partially apply would be to use either `curry2` or one of the section functions.

Oh, I see. It's not currently possible to define *uncurried* functions with sanctuary-def, but we could certainly address this limitation.

| Haskell           | Proposed in #388  | Proposed by Gabe  |
| ----------------- | ----------------- | ----------------- |
| `(< y)`           | `S.lt(y)`         | `S.r_(S.lt, y)`   |
| `(x <)`           | `S.lt_(x)`        | `S.l_(S.lt, x)`   |
| `x < y`           | `S.lt_(x, y)`     | `S.lt(x, y)`      |

I prefer the middle column to the right column. The downside of the approach proposed in #388 is that we'll end up with lots of `_`-suffixed functions. This doesn't particularly worry me.

Gabe, are you happy to proceed with the plan set out in #388, or would you prefer to give the `l_`/`r_`/`in_` option further consideration? I'm happy to give you more time to persuade me. :)

gabejohnson: David, I've revised my proposal again after playing around with Haskell this evening.

| Haskell | Proposed in #388 | Proposed by Me |
| ------- | ------------------ | ---------------- |
| `(< y)` | `S.lt(y)`                 |  `S._r(S.lt, y)`     |
| `(x <)` | `S.lt_(x)`                |  `S._l(x, S.lt)`     |
| `x < y` | `S.lt_(x, y)`           |  `S._in(x, S.lt, y)`|
| `(<) x` | `S.lt_(x)`               |  `S.lt(x)`              |

I've changed `{in, l, r}_` to `_{in, l, r}` as I found it more visually pleasing.

Also note that `S._r` has the value on the _right_ and the operator on the left while `S._l` has the value on the _left_ and operator on the right. 

`S._in` allows for _explicit_ infix forms. All that's required is that the (currently) binary curried functions become unary functions that return unary functions.

Looking at the table, my proposal appears a little clunky. But I rarely fully qualify functions that I import in my own work and am more likely to use them as follow.
```js
const {compose, _in, _l, _r, div, gte, lt, sub, Just, Nothing} = S;

const lt5 = _r(lt, 5);
const _3minus = _l(3, sub);

compose(lt5, _3minus)( 12 / 3 ); // true
_in(Just(12), gte, Nothing); // true
```

I'd like to see if anyone else has opinions on this before moving on. It's been a point of contention in Sanctuary as well as Ramda for quite some time. It even tripped me up today using lodash/fp :blush:

I'm going to reference this comment in #239 and https://github.com/ramda/ramda/issues/1497 to see what others think.
CrossEye: @gabejohnson 
> I've revised my proposal again after playing around with Haskell this evening.

This is the best proposal I've yet seen in this never-ending debate.  I will try to make a PR for Ramda using this, unless you want to do it.

tycho01: @gabejohnson: so the proposed `S.lt(x)` means "greater or equal to x", right? That still feels a bit surprising. 😕
gabejohnson: @tycho01, I don't know what to tell you. Sanctuary, Ramda and Folktale all currently have that behavior. Vanilla lodash does as well. lodash/fp flips the argument order. fnuc changes the order depending on the number of arguments passed.

Additionally, LISPs prefix everything and have the same operator order `(< x y)` is equivalent to `x + y`. Haskell and Purescript do as well when switching to prefix mode.

I think that if the "section" functions are highlighted and well explained before encountering any associative, non-commutative binary functions, things should be fine. That is, as long as there is total consistency.
gabejohnson: @CrossEye go for it. I doubt I'll have time for a few weeks anyway.

Patch:
diff --git a/index.js b/index.js
index b2b3537..1f4d9de 100644
--- a/index.js
+++ b/index.js
@@ -167,23 +167,25 @@
 //. silent failures due to type coercion (at worst). For example:
 //.
 //. ```javascript
-//. S.inc('XXX');
+//. S.add(2, true);
 //. // ! TypeError: Invalid value
 //. //
-//. //   inc :: FiniteNumber -> FiniteNumber
-//. //          ^^^^^^^^^^^^
-//. //               1
+//. //   add :: FiniteNumber -> FiniteNumber -> FiniteNumber
+//. //                          ^^^^^^^^^^^^
+//. //                               1
 //. //
-//. //   1)  "XXX" :: String
+//. //   1)  true :: Boolean
 //. //
 //. //   The value at position 1 is not a member of ‘FiniteNumber’.
+//. //
+//. //   See https://github.com/sanctuary-js/sanctuary-def/tree/v0.11.0#FiniteNumber for information about the sanctuary-def/FiniteNumber type.
 //. ```
 //.
 //. Compare this to the behaviour of Ramda's unchecked equivalent:
 //.
 //. ```javascript
-//. R.inc('XXX');
-//. // => NaN
+//. R.add(2, true);
+//. // => 3
 //. ```
 //.
 //. There is a performance cost to run-time type checking. One may wish to
@@ -236,6 +238,15 @@
   //  Thunk :: Type -> Type
   function Thunk(x) { return $.Function([x]); }
 
+  //  flip$ :: ((a, b) -> c) -> b -> a -> c
+  function flip$(f) {
+    return function(x) {
+      return function(y) {
+        return f(y, x);
+      };
+    };
+  }
+
   //  typeEq :: String -> a -> Boolean
   function typeEq(typeIdent) {
     return function(x) {
@@ -628,7 +639,7 @@
   //.     (b -> c) -> (a -> b) -> (a -> c)
   //.
   //. ```javascript
-  //. > S.map(Math.sqrt, S.inc)(99)
+  //. > S.map(Math.sqrt, S.add(1))(99)
   //. 10
   //. ```
   S.map = def('map', {f: [Z.Functor]}, [Fn(a, b), f(a), f(b)], Z.map);
@@ -655,7 +666,7 @@
   //. Curried version of [`Z.promap`][].
   //.
   //. ```javascript
-  //. > S.promap(Math.abs, S.inc, Math.sqrt)(-100)
+  //. > S.promap(Math.abs, S.add(1), Math.sqrt)(-100)
   //. 11
   //. ```
   S.promap =
@@ -785,7 +796,7 @@
   //. > S.ap([Math.sqrt, x => x * x], [1, 4, 9, 16, 25])
   //. [1, 2, 3, 4, 5, 1, 16, 81, 256, 625]
   //.
-  //. > S.ap({x: Math.sqrt, y: S.inc, z: S.dec}, {w: 4, x: 4, y: 4})
+  //. > S.ap({x: Math.sqrt, y: S.add(1), z: S.sub(1)}, {w: 4, x: 4, y: 4})
   //. {x: 2, y: 5}
   //.
   //. > S.ap(S.Just(Math.sqrt), S.Just(64))
@@ -1154,10 +1165,10 @@
   //. function.
   //.
   //. ```javascript
-  //. > S.A(S.inc, 42)
+  //. > S.A(S.add(1), 42)
   //. 43
   //.
-  //. > S.map(S.A(S.__, 100), [S.inc, Math.sqrt])
+  //. > S.map(S.A(S.__, 100), [S.add(1), Math.sqrt])
   //. [101, 10]
   //. ```
   function A(f, x) {
@@ -1172,10 +1183,10 @@
   //. `(&)` function.
   //.
   //. ```javascript
-  //. > S.T(42, S.inc)
+  //. > S.T(42, S.add(1))
   //. 43
   //.
-  //. > S.map(S.T(100), [S.inc, Math.sqrt])
+  //. > S.map(S.T(100), [S.add(1), Math.sqrt])
   //. [101, 10]
   //. ```
   function T(x, f) {
@@ -1320,7 +1331,7 @@
   //. See also [`pipe`](#pipe).
   //.
   //. ```javascript
-  //. > S.compose(Math.sqrt, S.inc)(99)
+  //. > S.compose(Math.sqrt, S.add(1), 99)
   //. 10
   //. ```
   function compose(f, g, x) {
@@ -1338,7 +1349,7 @@
   //. of functions. `pipe([f, g, h], x)` is equivalent to `h(g(f(x)))`.
   //.
   //. ```javascript
-  //. > S.pipe([S.inc, Math.sqrt, S.dec])(99)
+  //. > S.pipe([S.add(1), Math.sqrt, S.sub(1)], 99)
   //. 9
   //. ```
   function pipe(fs, x) {
@@ -2219,10 +2230,10 @@
   //. left side as well as the right side.
   //.
   //. ```javascript
-  //. > S.bimap(S.toUpper, S.inc, S.Left('abc'))
+  //. > S.bimap(S.toUpper, S.add(1), S.Left('abc'))
   //. Left('ABC')
   //.
-  //. > S.bimap(S.toUpper, S.inc, S.Right(42))
+  //. > S.bimap(S.toUpper, S.add(1), S.Right(42))
   //. Right(43)
   //. ```
   Either.prototype['fantasy-land/bimap'] = function(f, g) {
@@ -2498,9 +2509,6 @@
   //
   //. > S.tagBy(S.odd, 1)
   //. Right(1)
-  //
-  //. > S.compose(S.bimap(S.dec, S.inc), S.tagBy(S.odd))(5)
-  //. Right(6)
   //. ```
   function tagBy(pred, a) {
     return pred(a) ? Right(a) : Left(a);
@@ -3527,45 +3535,37 @@
   S.sum =
   def('sum', {f: [Z.Foldable]}, [f($.FiniteNumber), $.FiniteNumber], sum);
 
-  //# sub :: FiniteNumber -> FiniteNumber -> FiniteNumber
+  //# sub :: FiniteNumber -> (FiniteNumber -> FiniteNumber)
   //.
-  //. Returns the difference between two (finite) numbers.
+  //. Takes a finite number `n` and returns the _subtract `n`_ function.
+  //.
+  //. See also [`sub_`](#sub_).
   //.
   //. ```javascript
-  //. > S.sub(4, 2)
-  //. 2
+  //. > S.map(S.sub(1), [1, 2, 3])
+  //. [0, 1, 2]
   //. ```
-  function sub(x, y) {
-    return x - y;
-  }
   S.sub =
-  def('sub', {}, [$.FiniteNumber, $.FiniteNumber, $.FiniteNumber], sub);
+  def('sub',
+      {},
+      [$.FiniteNumber, Fn($.FiniteNumber, $.FiniteNumber)],
+      flip$(sub_));
 
-  //# inc :: FiniteNumber -> FiniteNumber
-  //.
-  //. Increments a (finite) number by one.
+  //# sub_ :: FiniteNumber -> FiniteNumber -> FiniteNumber
   //.
-  //. ```javascript
-  //. > S.inc(1)
-  //. 2
-  //. ```
-  function inc(x) {
-    return x + 1;
-  }
-  S.inc = def('inc', {}, [$.FiniteNumber, $.FiniteNumber], inc);
-
-  //# dec :: FiniteNumber -> FiniteNumber
+  //. Returns the difference between two (finite) numbers.
   //.
-  //. Decrements a (finite) number by one.
+  //. See also [`sub`](#sub).
   //.
   //. ```javascript
-  //. > S.dec(2)
-  //. 1
+  //. > S.sub_(4, 2)
+  //. 2
   //. ```
-  function dec(x) {
-    return x - 1;
+  function sub_(x, y) {
+    return x - y;
   }
-  S.dec = def('dec', {}, [$.FiniteNumber, $.FiniteNumber], dec);
+  S.sub_ =
+  def('sub_', {}, [$.FiniteNumber, $.FiniteNumber, $.FiniteNumber], sub_);
 
   //# mult :: FiniteNumber -> FiniteNumber -> FiniteNumber
   //.


Test patch:
diff --git a/test/A.js b/test/A.js
index e3f1d5d..0cf6be2 100644
--- a/test/A.js
+++ b/test/A.js
@@ -12,7 +12,7 @@ test('A', function() {
   eq(S.A.length, 2);
   eq(S.A.toString(), 'A :: (a -> b) -> a -> b');
 
-  eq(S.A(S.inc, 1), 2);
-  eq(map(S.A(S.__, 100))([S.inc, Math.sqrt]), [101, 10]);
+  eq(S.A(S.add(1), 1), 2);
+  eq(map(S.A(S.__, 100))([S.add(1), Math.sqrt]), [101, 10]);
 
 });
diff --git a/test/Either/Left.js b/test/Either/Left.js
index 26d7013..ec4daf6 100644
--- a/test/Either/Left.js
+++ b/test/Either/Left.js
@@ -28,12 +28,12 @@ suite('Left', function() {
   test('"fantasy-land/ap" method', function() {
     eq(S.Left('abc')[FL.ap].length, 1);
     eq(S.Left('abc')[FL.ap](S.Left('xyz')), S.Left('xyz'));
-    eq(S.Left('abc')[FL.ap](S.Right(S.inc)), S.Left('abc'));
+    eq(S.Left('abc')[FL.ap](S.Right(S.add(1))), S.Left('abc'));
   });
 
   test('"fantasy-land/bimap" method', function() {
     eq(S.Left('abc')[FL.bimap].length, 2);
-    eq(S.Left('abc')[FL.bimap](S.toUpper, S.inc), S.Left('ABC'));
+    eq(S.Left('abc')[FL.bimap](S.toUpper, S.add(1)), S.Left('ABC'));
   });
 
   test('"fantasy-land/chain" method', function() {
diff --git a/test/Either/Right.js b/test/Either/Right.js
index 4e360c8..b89c630 100644
--- a/test/Either/Right.js
+++ b/test/Either/Right.js
@@ -28,12 +28,12 @@ suite('Right', function() {
   test('"fantasy-land/ap" method', function() {
     eq(S.Right(42)[FL.ap].length, 1);
     eq(S.Right(42)[FL.ap](S.Left('abc')), S.Left('abc'));
-    eq(S.Right(42)[FL.ap](S.Right(S.inc)), S.Right(43));
+    eq(S.Right(42)[FL.ap](S.Right(S.add(1))), S.Right(43));
   });
 
   test('"fantasy-land/bimap" method', function() {
     eq(S.Right(42)[FL.bimap].length, 2);
-    eq(S.Right(42)[FL.bimap](S.toUpper, S.inc), S.Right(43));
+    eq(S.Right(42)[FL.bimap](S.toUpper, S.add(1)), S.Right(43));
   });
 
   test('"fantasy-land/chain" method', function() {
diff --git a/test/Maybe/Just.js b/test/Maybe/Just.js
index 27a1068..05c9c5f 100644
--- a/test/Maybe/Just.js
+++ b/test/Maybe/Just.js
@@ -27,7 +27,7 @@ suite('Just', function() {
   test('"fantasy-land/ap" method', function() {
     eq(S.Just(42)[FL.ap].length, 1);
     eq(S.Just(42)[FL.ap](S.Nothing), S.Nothing);
-    eq(S.Just(42)[FL.ap](S.Just(S.inc)), S.Just(43));
+    eq(S.Just(42)[FL.ap](S.Just(S.add(1))), S.Just(43));
   });
 
   test('"fantasy-land/chain" method', function() {
diff --git a/test/Maybe/Nothing.js b/test/Maybe/Nothing.js
index 0823f4a..51c9b18 100644
--- a/test/Maybe/Nothing.js
+++ b/test/Maybe/Nothing.js
@@ -25,7 +25,7 @@ suite('Nothing', function() {
   test('"fantasy-land/ap" method', function() {
     eq(S.Nothing[FL.ap].length, 1);
     eq(S.Nothing[FL.ap](S.Nothing), S.Nothing);
-    eq(S.Nothing[FL.ap](S.Just(S.inc)), S.Nothing);
+    eq(S.Nothing[FL.ap](S.Just(S.add(1))), S.Nothing);
   });
 
   test('"fantasy-land/chain" method', function() {
diff --git a/test/T.js b/test/T.js
index ab7ccc4..cf39293 100644
--- a/test/T.js
+++ b/test/T.js
@@ -12,7 +12,7 @@ test('T', function() {
   eq(S.T.length, 2);
   eq(S.T.toString(), 'T :: a -> (a -> b) -> b');
 
-  eq(S.T(42, S.inc), 43);
-  eq(map(S.T(100))([S.inc, Math.sqrt]), [101, 10]);
+  eq(S.T(42, S.add(1)), 43);
+  eq(map(S.T(100))([S.add(1), Math.sqrt]), [101, 10]);
 
 });
diff --git a/test/ap.js b/test/ap.js
index ab4de01..a75345c 100644
--- a/test/ap.js
+++ b/test/ap.js
@@ -13,14 +13,14 @@ test('ap', function() {
 
   eq(S.ap([], []), []);
   eq(S.ap([], [1, 2, 3]), []);
-  eq(S.ap([S.inc], []), []);
-  eq(S.ap([S.inc], [1, 2, 3]), [2, 3, 4]);
-  eq(S.ap([S.dec, Math.sqrt], [1, 4, 9]), [0, 3, 8, 1, 2, 3]);
+  eq(S.ap([S.add(1)], []), []);
+  eq(S.ap([S.add(1)], [1, 2, 3]), [2, 3, 4]);
+  eq(S.ap([S.sub(1), Math.sqrt], [1, 4, 9]), [0, 3, 8, 1, 2, 3]);
   eq(S.ap({}, {}), {});
   eq(S.ap({}, {x: 1, y: 2, z: 3}), {});
-  eq(S.ap({x: S.inc}, {}), {});
-  eq(S.ap({x: S.inc}, {x: 1}), {x: 2});
-  eq(S.ap({x: S.inc, y: S.dec, z: Math.sqrt}, {w: 0, x: 1, y: 2}), {x: 2, y: 1});
+  eq(S.ap({x: S.add(1)}, {}), {});
+  eq(S.ap({x: S.add(1)}, {x: 1}), {x: 2});
+  eq(S.ap({x: S.add(1), y: S.sub(1), z: Math.sqrt}, {w: 0, x: 1, y: 2}), {x: 2, y: 1});
   eq(S.ap(S.Nothing, S.Nothing), S.Nothing);
   eq(S.ap(S.Nothing, S.Just(9)), S.Nothing);
   eq(S.ap(S.Just(Math.sqrt), S.Nothing), S.Nothing);
diff --git a/test/bimap.js b/test/bimap.js
index f032e70..c9c067f 100644
--- a/test/bimap.js
+++ b/test/bimap.js
@@ -11,7 +11,7 @@ test('bimap', function() {
   eq(S.bimap.length, 3);
   eq(S.bimap.toString(), 'bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d');
 
-  eq(S.bimap(S.toUpper, S.inc, S.Left('xxx')), S.Left('XXX'));
-  eq(S.bimap(S.toUpper, S.inc, S.Right(1000)), S.Right(1001));
+  eq(S.bimap(S.toUpper, S.add(1), S.Left('xxx')), S.Left('XXX'));
+  eq(S.bimap(S.toUpper, S.add(1), S.Right(1000)), S.Right(1001));
 
 });
diff --git a/test/compose.js b/test/compose.js
index aabdb4c..868c698 100644
--- a/test/compose.js
+++ b/test/compose.js
@@ -11,6 +11,6 @@ test('compose', function() {
   eq(S.compose.length, 3);
   eq(S.compose.toString(), 'compose :: (b -> c) -> (a -> b) -> a -> c');
 
-  eq(S.compose(S.mult(2), S.inc, 20), 42);
+  eq(S.compose(S.mult(2), S.add(1), 20), 42);
 
 });
diff --git a/test/create.js b/test/create.js
index 549f30f..627b55a 100644
--- a/test/create.js
+++ b/test/create.js
@@ -32,8 +32,8 @@ test('create', function() {
   eq(Object.keys(uncheckedDefaultEnv).sort(), expected);
   eq(Object.keys(uncheckedCustomEnv).sort(), expected);
 
-  eq(uncheckedDefaultEnv.inc(42), S.inc(42));
-  eq(uncheckedDefaultEnv.inc('XXX'), 'XXX1');
+  eq(uncheckedDefaultEnv.add(1, 42), S.add(1, 42));
+  eq(uncheckedDefaultEnv.add(1, 'XXX'), '1XXX');
 
   throws(function() { S.I(['foo', 'foo', 42]); },
          TypeError,
diff --git a/test/dec.js b/test/dec.js
deleted file mode 100644
index a03f92d..0000000
--- a/test/dec.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict';
-
-var S = require('..');
-
-var eq = require('./internal/eq');
-
-
-test('dec', function() {
-
-  eq(typeof S.dec, 'function');
-  eq(S.dec.length, 1);
-  eq(S.dec.toString(), 'dec :: FiniteNumber -> FiniteNumber');
-
-  eq(S.dec(2), 1);
-  eq(S.dec(-1), -2);
-  eq(S.dec(1.5), 0.5);
-  eq(S.dec(-1.5), -2.5);
-
-});
diff --git a/test/ifElse.js b/test/ifElse.js
index 8f14f35..e085479 100644
--- a/test/ifElse.js
+++ b/test/ifElse.js
@@ -11,7 +11,7 @@ test('ifElse', function() {
   eq(S.ifElse.length, 4);
   eq(S.ifElse.toString(), 'ifElse :: (a -> Boolean) -> (a -> b) -> (a -> b) -> a -> b');
 
-  eq(S.ifElse(S.odd, S.dec, S.inc, 9), 8);
-  eq(S.ifElse(S.odd, S.dec, S.inc, 0), 1);
+  eq(S.ifElse(S.odd, S.sub(1), S.add(1), 9), 8);
+  eq(S.ifElse(S.odd, S.sub(1), S.add(1), 0), 1);
 
 });
diff --git a/test/inc.js b/test/inc.js
deleted file mode 100644
index 399edc6..0000000
--- a/test/inc.js
+++ /dev/null
@@ -1,19 +0,0 @@
-'use strict';
-
-var S = require('..');
-
-var eq = require('./internal/eq');
-
-
-test('inc', function() {
-
-  eq(typeof S.inc, 'function');
-  eq(S.inc.length, 1);
-  eq(S.inc.toString(), 'inc :: FiniteNumber -> FiniteNumber');
-
-  eq(S.inc(1), 2);
-  eq(S.inc(-1), 0);
-  eq(S.inc(1.5), 2.5);
-  eq(S.inc(-1.5), -0.5);
-
-});
diff --git a/test/lift3.js b/test/lift3.js
index 67f5090..0eb4555 100644
--- a/test/lift3.js
+++ b/test/lift3.js
@@ -21,6 +21,6 @@ test('lift3', function() {
   eq(S.lift3(S.reduce, [S.add], [0], [[1, 2, 3]]), [6]);
   eq(S.lift3(S.reduce, [S.add], [0], []), []);
 
-  eq(S.lift3(area, S.dec, S.I, S.inc)(4), 6);
+  eq(S.lift3(area, S.sub(1), S.I, S.add(1))(4), 6);
 
 });
diff --git a/test/pipe.js b/test/pipe.js
index 6dedb35..5c51a5a 100644
--- a/test/pipe.js
+++ b/test/pipe.js
@@ -13,8 +13,8 @@ test('pipe', function() {
 
   eq(S.pipe([], '99'), '99');
   eq(S.pipe([parseInt], '99'), 99);
-  eq(S.pipe([parseInt, S.inc], '99'), 100);
-  eq(S.pipe([parseInt, S.inc, Math.sqrt], '99'), 10);
-  eq(S.pipe([parseInt, S.inc, Math.sqrt, S.dec], '99'), 9);
+  eq(S.pipe([parseInt, S.add(1)], '99'), 100);
+  eq(S.pipe([parseInt, S.add(1), Math.sqrt], '99'), 10);
+  eq(S.pipe([parseInt, S.add(1), Math.sqrt, S.sub(1)], '99'), 9);
 
 });
diff --git a/test/promap.js b/test/promap.js
index ad23010..a7a06b5 100644
--- a/test/promap.js
+++ b/test/promap.js
@@ -15,6 +15,6 @@ test('promap', function() {
   var after = S.flip_(Math.pow)(2);
   eq(S.promap(before, after, S.sum)(['foo', 'bar', 'baz', 'quux']), 169);
 
-  eq(S.promap(Math.abs, S.inc, Math.sqrt)(-100), 11);
+  eq(S.promap(Math.abs, S.add(1), Math.sqrt)(-100), 11);
 
 });
diff --git a/test/sub.js b/test/sub.js
index 9314cde..bf296a4 100644
--- a/test/sub.js
+++ b/test/sub.js
@@ -8,12 +8,9 @@ var eq = require('./internal/eq');
 test('sub', function() {
 
   eq(typeof S.sub, 'function');
-  eq(S.sub.length, 2);
-  eq(S.sub.toString(), 'sub :: FiniteNumber -> FiniteNumber -> FiniteNumber');
+  eq(S.sub.length, 1);
+  eq(S.sub.toString(), 'sub :: FiniteNumber -> (FiniteNumber -> FiniteNumber)');
 
-  eq(S.sub(1, 1), 0);
-  eq(S.sub(-1, -1), 0);
-  eq(S.sub(7.5, 2), 5.5);
-  eq(S.sub(-7.5, -2), -5.5);
+  eq(S.map(S.sub(1), [1, 2, 3]), [0, 1, 2]);
 
 });
diff --git a/test/sub_.js b/test/sub_.js
new file mode 100644
index 0000000..f73ff6e
--- /dev/null
+++ b/test/sub_.js
@@ -0,0 +1,19 @@
+'use strict';
+
+var S = require('..');
+
+var eq = require('./internal/eq');
+
+
+test('sub_', function() {
+
+  eq(typeof S.sub_, 'function');
+  eq(S.sub_.length, 2);
+  eq(S.sub_.toString(), 'sub_ :: FiniteNumber -> FiniteNumber -> FiniteNumber');
+
+  eq(S.sub_(1, 1), 0);
+  eq(S.sub_(-1, -1), 0);
+  eq(S.sub_(7.5, 2), 5.5);
+  eq(S.sub_(-7.5, -2), -5.5);
+
+});