Skip to content

Commit

Permalink
fix(liftFN): fix multiple bugs and resolve ramda complatibility issue
Browse files Browse the repository at this point in the history
Closes #59
  • Loading branch information
char0n committed Apr 23, 2017
1 parent 54e7702 commit 47bc23f
Show file tree
Hide file tree
Showing 6 changed files with 80 additions and 24 deletions.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"name": "ramda-adjunct",
"description": "Extensions for Ramda",
"keywords": "ramda extensions addons cookbook adjunct recipe extras",
"version": "1.4.0",
"version": "1.3.2",
"homepage": "https://github.com/char0n/ramda-adjunct",
"license": "SEE LICENSE IN LICENSE.md",
"repository": {
Expand Down Expand Up @@ -104,5 +104,8 @@
"typescript": "=2.2.2",
"webpack": "=2.4.1"
},
"tonicExampleFilename": "tonicExample.js"
"tonicExampleFilename": "tonicExample.js",
"dependencies": {
"fantasy-land": "=3.2.0"
}
}
4 changes: 2 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,13 +234,13 @@ declare namespace RamdaAdjunct {

/**
* "lifts" a function to be the specified arity, so that it may "map over" objects that satisfy
* the Apply spec of algebraic structures.
* the Apply spec of fantasy land.
*/
liftFN<T>(arity: number, fn: Variadic<Apply, T>): Apply

/**
* "lifts" a function of arity > 1 so that it may "map over" objects that satisfy
* the Apply spec of algebraic structures.
* the Apply spec of fantasy land.
*/
liftF<T>(fn: Variadic<Apply, T>): Apply
}
Expand Down
11 changes: 6 additions & 5 deletions src/liftF.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import liftFN from './liftFN';

/**
* "lifts" a function of arity > 1 so that it may "map over" objects that satisfy
* the Apply spec of algebraic structures. This function is not compatible
* with {@link https://github.com/fantasyland/fantasy-land#apply|FantasyLand Apply spec}.
* "lifts" a function to be the specified arity, so that it may "map over" objects that satisfy
* the fantasy land Apply spec of algebraic structures.
*
* Lifting is specific for {@link https://github.com/scalaz/scalaz|scalaz} and {@link http://www.functionaljava.org/|functional java} implementations.
* One of the mainstream libraries that uses this Apply spec is {@link https://cwmyers.github.io/monet.js/|monet.js}.
* This function acts as interop for ramda and monet.js.
* Old version of fantasy land spec were not compatible with this approach,
* but as of fantasy land 1.0.0 Apply spec also adopted this approach.
*
* This function acts as interop for ramda <= 0.23.0 and {@link https://cwmyers.github.io/monet.js/|monet.js}.
*
* More info {@link https://github.com/fantasyland/fantasy-land/issues/50|here}.
*
Expand Down
57 changes: 49 additions & 8 deletions src/liftFN.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,54 @@
import { curry, last, slice, reverse, reduce, pipe, ap, curryN, map, flip } from 'ramda';
import fantasyLand from 'fantasy-land';
import { curry, head, slice, reduce, ap as apR, curryN, map, add, flip } from 'ramda';


const applicativeTrait = {
of(value) {
return { ...this, value };
},
};
applicativeTrait[fantasyLand.of] = applicativeTrait.of;

const functorTrait = {
map(fn) {
return this.of(fn(this.value));
},
};
functorTrait[fantasyLand.map] = functorTrait.map;

const applyTrait = {
ap(monadWithApplyOfAFunction) {
return monadWithApplyOfAFunction.map(fn => fn(this.value));
},
};
applyTrait[fantasyLand.ap] = applyTrait.ap;

const Monad = { ...applicativeTrait, ...functorTrait, ...applyTrait };
const m1 = Monad.of(1);
const m2 = Monad.of(2).map(add);


export const createAp = (ap1, ap2) => {
try {
// new version of `ap` starting from ramda version > 0.23.0
return ap1.ap(ap2) && apR;
} catch (e) {
// old version of `ap` till ramda version <= 0.23.0
return curryN(2, flip(apR));
}
};

const ap = createAp(m2, m1);

/**
* "lifts" a function to be the specified arity, so that it may "map over" objects that satisfy
* the Apply spec of algebraic structures. This function is not compatible
* with {@link https://github.com/fantasyland/fantasy-land#apply|FantasyLand Apply spec}.
* the fantasy land Apply spec of algebraic structures.
*
* Lifting is specific for {@link https://github.com/scalaz/scalaz|scalaz} and {@link http://www.functionaljava.org/|functional java} implementations.
* One of the mainstream libraries that uses this Apply spec is {@link https://cwmyers.github.io/monet.js/|monet.js}.
* This function acts as interop for ramda and monet.js.
* Old version of fantasy land spec were not compatible with this approach,
* but as of fantasy land 1.0.0 Apply spec also adopted this approach.
*
* This function acts as interop for ramda <= 0.23.0 and {@link https://cwmyers.github.io/monet.js/|monet.js}.
*
* More info {@link https://github.com/fantasyland/fantasy-land/issues/50|here}.
*
Expand All @@ -33,9 +74,9 @@ import { curry, last, slice, reverse, reduce, pipe, ap, curryN, map, flip } from
const liftFN = curry((arity, fn) => {
const lifted = curryN(arity, fn);
return curryN(arity, (...args) => {
const accumulator = map(lifted, last(args));
const apps = pipe(slice(0, arity - 1), reverse)(args);
return reduce(flip(ap), accumulator, apps);
const accumulator = map(lifted, head(args));
const apps = slice(1, Infinity, args);
return reduce(ap, accumulator, apps);
});
});

Expand Down
7 changes: 3 additions & 4 deletions test/liftF.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Maybe } from 'monet';
import { curry } from 'ramda';

import RA from '../src/index';
import eq from './shared/eq';

const add3 = curry((a, b, c) => a + b + c);
const add4 = curry((a, b, c, d) => a + b + c + d);
const add5 = curry((a, b, c, d, e) => a + b + c + d + e);
const add3 = (a, b, c) => a + b + c;
const add4 = (a, b, c, d) => a + b + c + d;
const add5 = (a, b, c, d, e) => a + b + c + d + e;

describe('liftF', function() {
const addF3 = RA.liftF(add3);
Expand Down
18 changes: 15 additions & 3 deletions test/liftFN.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import chai from 'chai';
import { Maybe } from 'monet';
import { add, reduce, curry } from 'ramda';
import { add, reduce } from 'ramda';

import RA from '../src/index';
import eq from './shared/eq';


const addN = (...args) => reduce(add, 0, args);
const add3 = curry((a, b, c) => a + b + c);
const add3 = (a, b, c) => a + b + c;

describe('liftFN', function() {
const addN3 = RA.liftFN(3, addN);
Expand All @@ -18,7 +19,14 @@ describe('liftFN', function() {
});

it('limits a variadic function to the specified arity', function() {
eq(addN3(Maybe.Some(1), Maybe.Some(1), Maybe.Some(1), Maybe.Some(1)), Maybe.Some(3));
eq(addN3(Maybe.Some(1), Maybe.Some(1), Maybe.Some(1)), Maybe.Some(3));
});

it('throws error on variadic function is more arguments than arity', function() {
chai.assert.throws(
addN3.bind(null, Maybe.Some(1), Maybe.Some(1), Maybe.Some(1), Maybe.Some(1)),
TypeError
);
});

it('can lift functions of any arity', function() {
Expand All @@ -30,6 +38,10 @@ describe('liftFN', function() {
);
});

it('retain order of arguments', function() {
eq(RA.liftFN(3, add3)(Maybe.Some('a'), Maybe.Some('b'), Maybe.Some('c')), Maybe.Some('abc'));
});

it('is curried', function() {
const f4 = RA.liftFN(4);
eq(typeof f4, 'function');
Expand Down

0 comments on commit 47bc23f

Please sign in to comment.