Repository: plaid/sanctuary
Pull request #112 (2015-11-06 10:25:56)

Add ifElse function
I tried to avoid using `S.test` again by using `Just#equals` but that function depends on `this` so the context got messed up when passed into `R.ifElse`.

svozza: Is there anything else here that needs tidying up?
davidchambers: Let's use the expressions used in the doctests in the unit tests themselves. `S.K('light')` ignores its argument, obfuscating the behaviour of `ifElse`.

Also, please squash the commits.

svozza: Done!
davidchambers: I just thought of one more thing. We should update this line:

```javascript
'{ord}': R.nth(_, ['first', 'second', 'third']),
```

We should include `'fourth'` here (even though there are no constraints on the type of the fourth argument to `ifElse`).


Patch:
diff --git a/index.js b/index.js
index a67614b..943711e 100644
--- a/index.js
+++ b/index.js
@@ -150,7 +150,7 @@
                        R.nth(_, ['zero', 'one', 'two', 'three', 'four', 'five',
                                  'six', 'seven', 'eight', 'nine', 'ten']),
                        String),
-    '{ord}': R.nth(_, ['first', 'second', 'third']),
+    '{ord}': R.nth(_, ['first', 'second', 'third', 'fourth']),
     '{quote}': function(s) { return '\u2018' + s + '\u2019'; },
     '{repr}': R.toString,
     '{type}': functionName
@@ -212,6 +212,7 @@
       case 1: return function(a) { return f.apply(this, arguments); };
       case 2: return function(a, b) { return f.apply(this, arguments); };
       case 3: return function(a, b, c) { return f.apply(this, arguments); };
+      case 4: return function(a, b, c, d) { return f.apply(this, arguments); };
     }
   };
 
@@ -1497,6 +1498,25 @@
     return xBool !== yBool ? or(x, y) : xEmpty;
   });
 
+  //# ifElse :: (a -> Boolean) -> (a -> b) -> (a -> b) -> a -> b
+  //.
+  //. Takes a unary predicate, a unary "if" function, a unary "else"
+  //. function, and a value of any type, and returns the result of
+  //. applying the "if" function to the value if the value satisfies
+  //. the predicate; the result of applying the "else" function to the
+  //. value otherwise.
+  //.
+  // ```javascript
+  //. > S.ifElse(x => x < 0, Math.abs, Math.sqrt, -1)
+  //. 1
+  //.
+  //. > S.ifElse(x => x < 0, Math.abs, Math.sqrt, 16)
+  //. 4
+  //. ```
+  S.ifElse = def('ifElse', [Function, Function, Function, a], function(pred, f, g, x) {
+    return pred(x) ? f(x) : g(x);
+  });
+
   //. ### List
 
   //# slice :: Integer -> Integer -> [a] -> Maybe [a]


Test patch:
diff --git a/test/index.js b/test/index.js
index 1280cfe..61ddb01 100644
--- a/test/index.js
+++ b/test/index.js
@@ -1914,6 +1914,49 @@ describe('control', function() {
 
   });
 
+  describe('ifElse', function() {
+
+    var lt0 = function(x) { return x < 0; };
+
+    it('is a quaternary function', function() {
+      eq(typeof S.ifElse, 'function');
+      eq(S.ifElse.length, 4);
+    });
+
+    it('type checks its arguments', function() {
+      assert.throws(function() { S.ifElse('wrong'); },
+                    errorEq(TypeError,
+                            '‘ifElse’ requires a value of type Function ' +
+                            'as its first argument; received "wrong"'));
+
+      assert.throws(function() { S.ifElse(lt0, 'wrong'); },
+                    errorEq(TypeError,
+                            '‘ifElse’ requires a value of type Function ' +
+                            'as its second argument; received "wrong"'));
+
+      assert.throws(function() { S.ifElse(lt0, Math.abs, 'wrong'); },
+                    errorEq(TypeError,
+                            '‘ifElse’ requires a value of type Function ' +
+                            'as its third argument; received "wrong"'));
+    });
+
+    it('applies the first function when the predicate returns true', function() {
+      eq(S.ifElse(lt0, Math.abs, Math.sqrt, -1), 1);
+    });
+
+    it('applies the second function when the predicate returns false', function() {
+      eq(S.ifElse(lt0, Math.abs, Math.sqrt, 16), 4);
+    });
+
+    it('is curried', function() {
+      eq(S.ifElse(lt0).length, 3);
+      eq(S.ifElse(lt0)(Math.abs).length, 2);
+      eq(S.ifElse(lt0)(Math.abs)(Math.sqrt).length, 1);
+      eq(S.ifElse(lt0)(Math.abs)(Math.sqrt)(-1), 1);
+    });
+
+  });
+
 });
 
 describe('list', function() {