Skip to content

Commit

Permalink
refactor: of now works with Applicatives (#3272)
Browse files Browse the repository at this point in the history
This change addresses the issue of `of` not being able to
work with Applicatives.

Fixes #3267

BREAKING CHANGE: `of` is now a binary function with a constructor as its first argument.
  • Loading branch information
customcommander committed Apr 25, 2022
1 parent a4998cf commit dec329d
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 45 deletions.
1 change: 0 additions & 1 deletion source/internal/_of.js

This file was deleted.

34 changes: 24 additions & 10 deletions source/of.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import _curry1 from './internal/_curry1.js';
import _of from './internal/_of.js';

import _curry2 from './internal/_curry2.js';

/**
* Returns a singleton array containing the value provided.
* Given a constructor and a value, returns a new instance of that constructor
* containing the value.
*
* Dispatches to the `fantasy-land/of` method of the constructor first (if present)
* or to the `of` method last (if present). When neither are present, wraps the
* value in an array.
*
* Note this `of` is different from the ES6 `of`; See
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/of
Expand All @@ -12,13 +15,24 @@ import _of from './internal/_of.js';
* @memberOf R
* @since v0.3.0
* @category Function
* @sig a -> [a]
* @param {*} x any value
* @return {Array} An array wrapping `x`.
* @sig (* -> {*}) -> a -> {a}
* @param {Object} Ctor A constructor
* @param {*} val any value
* @return {*} An instance of the `Ctor` wrapping `val`.
* @example
*
* R.of(null); //=> [null]
* R.of([42]); //=> [[42]]
* R.of(Array, 42); //=> [42]
* R.of(Array, [42]); //=> [[42]]
* R.of(Maybe, 42); //=> Maybe.Just(42)
*/
var of = _curry1(_of);
var of = _curry2(function of(Ctor, val) {
return (
typeof Ctor['fantasy-land/of'] === 'function'
? Ctor['fantasy-land/of'](val)
: typeof Ctor.of === 'function'
? Ctor.of(val)
: [val]
);
});

export default of;
15 changes: 10 additions & 5 deletions test/of.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
var S = require('sanctuary');

var R = require('../source/index.js');
var eq = require('./shared/eq.js');


describe('of', function() {
it('returns its argument as an Array', function() {
eq(R.of(100), [100]);
eq(R.of([100]), [[100]]);
eq(R.of(null), [null]);
eq(R.of(undefined), [undefined]);
eq(R.of([]), [[]]);
eq(R.of(Array, 100), [100]);
eq(R.of(Array, [100]), [[100]]);
eq(R.of(Array, null), [null]);
eq(R.of(Array, undefined), [undefined]);
eq(R.of(Array, []), [[]]);
});

it('dispatches to an available of method', function() {
eq(R.of(S.Maybe, 100), S.Just(100));
});
});
29 changes: 16 additions & 13 deletions test/sequence.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,36 @@ var R = require('../source/index.js');
var Id = require('./shared/Id.js');
var eq = require('./shared/eq.js');

var ofArray = R.of(Array);
var ofEither = R.of(S.Either);
var ofMaybe = R.of(S.Maybe);

describe('sequence', function() {

it('operates on a list of lists', function() {
eq(R.sequence(R.of, []), [[]]);
eq(R.sequence(R.of, [[], [1, 2, 3, 4]]), []);
eq(R.sequence(R.of, [[1], [2, 3, 4]]), [[1, 2], [1, 3], [1, 4]]);
eq(R.sequence(R.of, [[1, 2], [3, 4]]), [[1, 3], [1, 4], [2, 3], [2, 4]]);
eq(R.sequence(R.of, [[1, 2, 3], [4]]), [[1, 4], [2, 4], [3, 4]]);
eq(R.sequence(R.of, [[1, 2, 3, 4], []]), []);
eq(R.sequence(ofArray, []), [[]]);
eq(R.sequence(ofArray, [[], [1, 2, 3, 4]]), []);
eq(R.sequence(ofArray, [[1], [2, 3, 4]]), [[1, 2], [1, 3], [1, 4]]);
eq(R.sequence(ofArray, [[1, 2], [3, 4]]), [[1, 3], [1, 4], [2, 3], [2, 4]]);
eq(R.sequence(ofArray, [[1, 2, 3], [4]]), [[1, 4], [2, 4], [3, 4]]);
eq(R.sequence(ofArray, [[1, 2, 3, 4], []]), []);
});

it('operates on a list of applicatives', function() {
eq(R.sequence(S.Maybe.of, [S.Just(3), S.Just(4), S.Just(5)]), S.Just([3, 4, 5]));
eq(R.sequence(S.Maybe.of, [S.Just(3), S.Nothing(), S.Just(5)]), S.Nothing());
eq(R.sequence(ofMaybe, [S.Just(3), S.Just(4), S.Just(5)]), S.Just([3, 4, 5]));
eq(R.sequence(ofMaybe, [S.Just(3), S.Nothing(), S.Just(5)]), S.Nothing());
});

it('traverses left to right', function() {
eq(R.sequence(S.Either.of, [S.Right(1), S.Right(2)]), S.Right([1, 2]));
eq(R.sequence(S.Either.of, [S.Right(1), S.Left('XXX')]), S.Left('XXX'));
eq(R.sequence(S.Either.of, [S.Left('XXX'), S.Right(1)]), S.Left('XXX'));
eq(R.sequence(S.Either.of, [S.Left('XXX'), S.Left('YYY')]), S.Left('XXX'));
eq(R.sequence(ofEither, [S.Right(1), S.Right(2)]), S.Right([1, 2]));
eq(R.sequence(ofEither, [S.Right(1), S.Left('XXX')]), S.Left('XXX'));
eq(R.sequence(ofEither, [S.Left('XXX'), S.Right(1)]), S.Left('XXX'));
eq(R.sequence(ofEither, [S.Left('XXX'), S.Left('YYY')]), S.Left('XXX'));
});

it('dispatches to `sequence` method', function() {
eq(R.sequence(Id, [Id(1), Id(2), Id(3)]), Id([1, 2, 3]));
eq(R.sequence(R.of, Id([1, 2, 3])), [Id(1), Id(2), Id(3)]);
eq(R.sequence(ofArray, Id([1, 2, 3])), [Id(1), Id(2), Id(3)]);
});

});
28 changes: 16 additions & 12 deletions test/traverse.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,32 @@ var R = require('../source/index.js');
var Id = require('./shared/Id.js');
var eq = require('./shared/eq.js');

var ofArray = R.of(Array);
var ofEither = R.of(S.Either);
var ofMaybe = R.of(S.Maybe);


describe('traverse', function() {

it('operates on a list of lists', function() {
eq(R.traverse(R.of, R.map(R.add(10)), []), [[]]);
eq(R.traverse(R.of, R.map(R.add(10)), [[], [1, 2, 3, 4]]), []);
eq(R.traverse(R.of, R.map(R.add(10)), [[1], [2, 3, 4]]), [[11, 12], [11, 13], [11, 14]]);
eq(R.traverse(R.of, R.map(R.add(10)), [[1, 2], [3, 4]]), [[11, 13], [11, 14], [12, 13], [12, 14]]);
eq(R.traverse(R.of, R.map(R.add(10)), [[1, 2, 3], [4]]), [[11, 14], [12, 14], [13, 14]]);
eq(R.traverse(R.of, R.map(R.add(10)), [[1, 2, 3, 4], []]), []);
eq(R.traverse(ofArray, R.map(R.add(10)), []), [[]]);
eq(R.traverse(ofArray, R.map(R.add(10)), [[], [1, 2, 3, 4]]), []);
eq(R.traverse(ofArray, R.map(R.add(10)), [[1], [2, 3, 4]]), [[11, 12], [11, 13], [11, 14]]);
eq(R.traverse(ofArray, R.map(R.add(10)), [[1, 2], [3, 4]]), [[11, 13], [11, 14], [12, 13], [12, 14]]);
eq(R.traverse(ofArray, R.map(R.add(10)), [[1, 2, 3], [4]]), [[11, 14], [12, 14], [13, 14]]);
eq(R.traverse(ofArray, R.map(R.add(10)), [[1, 2, 3, 4], []]), []);
});

it('operates on a list of applicatives', function() {
eq(R.traverse(S.Maybe.of, R.map(R.add(10)), [S.Just(3), S.Just(4), S.Just(5)]), S.Just([13, 14, 15]));
eq(R.traverse(S.Maybe.of, R.map(R.add(10)), [S.Just(3), S.Nothing(), S.Just(5)]), S.Nothing());
eq(R.traverse(ofMaybe, R.map(R.add(10)), [S.Just(3), S.Just(4), S.Just(5)]), S.Just([13, 14, 15]));
eq(R.traverse(ofMaybe, R.map(R.add(10)), [S.Just(3), S.Nothing(), S.Just(5)]), S.Nothing());
});

it('traverses left to right', function() {
eq(R.traverse(S.Either.of, R.identity, [S.Right(1), S.Right(2)]), S.Right([1, 2]));
eq(R.traverse(S.Either.of, R.identity, [S.Right(1), S.Left('XXX')]), S.Left('XXX'));
eq(R.traverse(S.Either.of, R.identity, [S.Left('XXX'), S.Right(1)]), S.Left('XXX'));
eq(R.traverse(S.Either.of, R.identity, [S.Left('XXX'), S.Left('YYY')]), S.Left('XXX'));
eq(R.traverse(ofEither, R.identity, [S.Right(1), S.Right(2)]), S.Right([1, 2]));
eq(R.traverse(ofEither, R.identity, [S.Right(1), S.Left('XXX')]), S.Left('XXX'));
eq(R.traverse(ofEither, R.identity, [S.Left('XXX'), S.Right(1)]), S.Left('XXX'));
eq(R.traverse(ofEither, R.identity, [S.Left('XXX'), S.Left('YYY')]), S.Left('XXX'));
});

it('dispatches to `traverse` method', function() {
Expand Down
10 changes: 6 additions & 4 deletions test/unless.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ var R = require('../source/index.js');
var eq = require('./shared/eq.js');
var _isArrayLike = require('../source/internal/_isArrayLike.js');

var ofArray = R.of(Array);


describe('unless', function() {
it('calls the whenFalse function if the validator returns a falsy value', function() {
eq(R.unless(_isArrayLike, R.of)(10), [10]);
eq(R.unless(_isArrayLike, ofArray)(10), [10]);
});

it('returns the argument unmodified if the validator returns a truthy value', function() {
eq(R.unless(_isArrayLike, R.of)([10]), [10]);
eq(R.unless(_isArrayLike, ofArray)([10]), [10]);
});

it('returns a curried function', function() {
eq(R.unless(_isArrayLike)(R.of)(10), [10]);
eq(R.unless(_isArrayLike)(R.of)([10]), [10]);
eq(R.unless(_isArrayLike)(ofArray)(10), [10]);
eq(R.unless(_isArrayLike)(ofArray)([10]), [10]);
});

});

0 comments on commit dec329d

Please sign in to comment.