main
Aaron Jensen 2015-08-12 08:35:10 -07:00
parent f32dba4a03
commit 571102d947
7 changed files with 149 additions and 22 deletions

View File

@ -1,6 +1,7 @@
# Change Log
## [unreleased]
* Add `u._` placeholder for curried functions.
## [0.6.0]
* Remove support for `_.placeholder` in curried methods. This may come back, but it was necessary for the next item.

View File

@ -163,6 +163,19 @@ var result = u({ person: { [key]: 21 } }, { person: { name: 'Olivier P.', age: 2
expect(result).toEqual({ person: { name: 'Olivier P.', age: 21 } });
```
### `u._`
All updeep functions are curried.
If you want to partially apply a function in an order other than the default argument order, you can use the placeholder.
```js
function increment(i) { return i + 1; }
var updateJoe = u(u._, { name: "Joe Merrill", age: 21 });
var result = updateJoe({ age: increment });
expect(result).toEqual({ name: "Joe Merrill", age: 22 });
```
### `u.updateIn(path(, value)(, object))`
Update a single value with a simple string or array path.

View File

@ -8,9 +8,11 @@ import reject from './reject';
import update from './update';
import updateIn from './updateIn';
import withDefault from './withDefault';
import { _ } from './util/curry';
const u = update;
u._ = _;
u.if = _if;
u.ifElse = ifElse;
u.is = is;

View File

@ -1,7 +1,20 @@
/* eslint no-shadow:0 */
/* eslint no-shadow:0, no-param-reassign:0 */
export const _ = '@@updeep/placeholder';
function countArguments(args, max) {
let n = args.length;
if (n > max) n = max;
while (args[n - 1] === _) {
n--;
}
return n;
}
export function curry1(fn) {
return function curried(a, b, c) {
const n = arguments.length;
const n = countArguments(arguments);
if (n >= 1) return fn(a, b, c);
return curried;
@ -10,9 +23,17 @@ export function curry1(fn) {
export function curry2(fn) {
return function curried(a, b, c, d) {
const n = arguments.length;
const n = countArguments(arguments, 2);
if (b === _ || c === _ || d === _) {
throw new Error('Can only use placeholder on first argument of this function.');
}
if (n >= 2) {
if (a === _) return curry1((a, c, d) => fn(a, b, c, d));
return fn(a, b, c, d);
}
if (n >= 2) return fn(a, b, c, d);
if (n === 1) return curry1((b, c, d) => fn(a, b, c, d));
return curried;
};
@ -20,22 +41,71 @@ export function curry2(fn) {
export function curry3(fn) {
return function curried(a, b, c, d, e) {
const n = arguments.length;
const n = countArguments(arguments, 3);
if (c === _ || d === _ || e === _) {
throw new Error('Can only use placeholder on first or second argument of this function.');
}
if (n >= 3) {
if (a === _) {
if (b === _) return curry2((a, b, d, e) => fn(a, b, c, d, e));
return curry1((a, d, e) => fn(a, b, c, d, e));
}
if (b === _) return curry1((b, d, e) => fn(a, b, c, d, e));
return fn(a, b, c, d, e);
}
if (n === 2) {
if (a === _) return curry2((a, c, d, e) => fn(a, b, c, d, e));
return curry1((c, d, e) => fn(a, b, c, d, e));
}
if (n >= 3) return fn(a, b, c, d, e);
if (n === 2) return curry1((c, d, e) => fn(a, b, c, d, e));
if (n === 1) return curry2((b, c, d, e) => fn(a, b, c, d, e));
return curried;
};
}
export function curry4(fn) {
return function curried(a, b, c, d, e, f) {
const n = arguments.length;
const n = countArguments(arguments, 4);
if (d === _ || e === _ || f === _) {
throw new Error('Can only use placeholder on first, second or third argument of this function.');
}
if (n >= 4) {
if (a === _) {
if (b === _) {
if (c === _) return curry3((a, b, c, e, f) => fn(a, b, c, d, e, f));
return curry2((a, b, e, f) => fn(a, b, c, d, e, f));
}
if (c === _) return curry2((a, c, e, f) => fn(a, b, c, d, e, f));
return curry1((a, e, f) => fn(a, b, c, d, e, f));
}
if (b === _) {
if (c === _) return curry2((b, c, e, f) => fn(a, b, c, d, e, f));
return curry1((b, e, f) => fn(a, b, c, d, e, f));
}
if (c === _) return curry1((c, e, f) => fn(a, b, c, d, e, f));
return fn(a, b, c, d, e, f);
}
if (n === 3) {
if (a === _) {
if (b === _) return curry3((a, b, d, e, f) => fn(a, b, c, d, e, f));
return curry2((a, d, e, f) => fn(a, b, c, d, e, f));
}
if (b === _) return curry2((b, d, e, f) => fn(a, b, c, d, e, f));
return curry1((d, e, f) => fn(a, b, c, d, e, f));
}
if (n === 2) {
if (a === _) return curry3((a, c, d, e, f) => fn(a, b, c, d, e, f));
return curry2((c, d, e, f) => fn(a, b, c, d, e, f));
}
if (n >= 4) return fn(a, b, c, d, e, f);
if (n === 3) return curry1((d, e, f) => fn(a, b, c, d, e, f));
if (n === 2) return curry2((c, d, e, f) => fn(a, b, c, d, e, f));
if (n === 1) return curry3((b, c, d, e, f) => fn(a, b, c, d, e, f));
return curried;
};

View File

@ -3,12 +3,12 @@ const Benchmark = require('benchmark');
const u = require('../lib');
const _ = require('lodash');
const { curry2 } = require('../lib/util/curry');
const { curry4 } = require('../lib/util/curry');
const add = (x, y) => x + y;
const add = (a, b, c, d) => a + b + c + d;
const fakeCurryAdd = x => y => x + y;
const curryAdd = _.curry(add);
const updeepCurry = curry2(add);
const updeepCurry = curry4(add);
// const updeepCurryBig = curry.curryBig(add);
const array = [0, 1, 2, 3, 4, 5];
@ -44,8 +44,8 @@ function createSuite(suiteName, tests) {
const curryVsLodash = createSuite('Curry', {
'updeep curry partial call': () => updeepCurry(3)(4),
'lodash curry partial call': () => curryAdd(3)(4),
'updeep curry partial call': () => updeepCurry(3)(4)(5)(6),
'lodash curry partial call': () => curryAdd(3)(4)(5)(6),
});
const mapVsLodash = createSuite('Map', {
@ -66,6 +66,6 @@ const applyVsDestructure = createSuite('apply vs destructure', {
'destructure': () => fnDestructure(1, 2, 3, 4, 5),
});
// curryVsLodash();
// mapVsLodash();
applyVsDestructure();
curryVsLodash();
mapVsLodash();
// applyVsDestructure();

View File

@ -110,4 +110,12 @@ describe('updeep', () => {
it('assigns null values', () => {
expect(u({isNull: null}, {})).to.eql({isNull: null});
});
it('can use a placeholder to partially apply', () => {
function increment(i) { return i + 1; }
const updateJoe = u(u._, { name: 'Joe Merrill', age: 21 });
const result = updateJoe({ age: increment });
expect(result).to.eql({ name: 'Joe Merrill', age: 22 });
});
});

View File

@ -1,5 +1,5 @@
import { expect } from 'chai';
import { curry1, curry2, curry3, curry4 } from '../../lib/util/curry';
import { curry1, curry2, curry3, curry4, _ } from '../../lib/util/curry';
describe('curry1', () => {
it('can curry one arguments', () => {
@ -26,12 +26,17 @@ describe('curry2', () => {
const curried = curry2((a, b, c, d) => [a, b, c, d]);
expect(curried(1, 2, 3, 4, 5)).to.eql([1, 2, 3, 4]);
});
it('can use the placeholder', () => {
const curried = curry2((a, b, c, d) => [a, b, c, d]);
expect(curried(_, 2)(1, 3, 4)).to.eql([1, 2, 3, 4]);
});
});
describe('curry3', () => {
it('can curry three arguments', () => {
const add = curry3((x, y, z) => x + y + z);
expect(add(3)(4)(5)).to.equal(12);
expect(add(3, _)(4)(5)).to.equal(12);
expect(add()(3)()(4, 5)).to.equal(12);
expect(add(3, 4, 5)).to.equal(12);
});
@ -40,12 +45,20 @@ describe('curry3', () => {
const curried = curry3((a, b, c, d, e) => [a, b, c, d, e]);
expect(curried(1, 2, 3, 4, 5, 6)).to.eql([1, 2, 3, 4, 5]);
});
it('can use the placeholder', () => {
const curried = curry3((a, b, c, d, e) => [a, b, c, d, e]);
expect(curried(_, 2)('a', 3, 4, 5)).to.eql(['a', 2, 3, 4, 5]);
expect(curried('b', _, 3)(2, 4, 5)).to.eql(['b', 2, 3, 4, 5]);
expect(curried(_, 2, 3)('c', 4, 5)).to.eql(['c', 2, 3, 4, 5]);
expect(curried(_, _, 3)('d', 2, 4, 5)).to.eql(['d', 2, 3, 4, 5]);
});
});
describe('curry4', () => {
it('can curry four arguments', () => {
const add = curry4((x, y, z, u) => x + y + z + u);
expect(add(3)(4)(5)(6)).to.equal(18);
expect(add(3, _)(4)(5)(6)).to.equal(18);
expect(add()(3)()(4, 5, 6)).to.equal(18);
expect(add(3, 4, 5, 6)).to.equal(18);
});
@ -54,4 +67,24 @@ describe('curry4', () => {
const curried = curry4((a, b, c, d, e, f) => [a, b, c, d, e, f]);
expect(curried(1, 2, 3, 4, 5, 6, 7)).to.eql([1, 2, 3, 4, 5, 6]);
});
it('can use the placeholder', () => {
const curried = curry4((a, b, c, d, e, f) => [a, b, c, d, e, f]);
expect(curried(_, 2)('a', 3, 4, 5, 6)).to.eql(['a', 2, 3, 4, 5, 6]);
expect(curried(_, 2, 3)('b', 4, 5, 6)).to.eql(['b', 2, 3, 4, 5, 6]);
expect(curried(_, 2, 3, 4)('c', 5, 6)).to.eql(['c', 2, 3, 4, 5, 6]);
expect(curried('d', _, 3)(2, 4, 5, 6)).to.eql(['d', 2, 3, 4, 5, 6]);
expect(curried('e', _, 3, 4)(2, 5, 6)).to.eql(['e', 2, 3, 4, 5, 6]);
expect(curried('f', 2, _, 4)(3, 5, 6)).to.eql(['f', 2, 3, 4, 5, 6]);
expect(curried(_, _, 3)('g', 2, 4, 5, 6)).to.eql(['g', 2, 3, 4, 5, 6]);
expect(curried(_, _, 3, 4)('h', 2, 5, 6)).to.eql(['h', 2, 3, 4, 5, 6]);
expect(curried(_, 2, _, 4)('i', 3, 5, 6)).to.eql(['i', 2, 3, 4, 5, 6]);
expect(curried('j', _, _, 4)(2, 3, 5, 6)).to.eql(['j', 2, 3, 4, 5, 6]);
expect(curried(_, _, _, 4)('k', 2, 3, 5, 6)).to.eql(['k', 2, 3, 4, 5, 6]);
});
});