Skip to content

Commit

Permalink
add S.sum and S.product
Browse files Browse the repository at this point in the history
  • Loading branch information
svozza committed Apr 24, 2016
1 parent e3c2db2 commit d26bc4e
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 21 deletions.
74 changes: 53 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@
var Foldable = $.TypeClass(
'sanctuary/Foldable',
function(x) {
return _type(x) === 'Array' || hasMethod('reduce');
return _type(x) === 'Array' || hasMethod('reduce')(x);
}
);

Expand Down Expand Up @@ -271,6 +271,7 @@
var b = $.TypeVariable('b');
var c = $.TypeVariable('c');
var d = $.TypeVariable('d');
var f = $.TypeVariable('f');
var l = $.TypeVariable('l');
var r = $.TypeVariable('r');

Expand Down Expand Up @@ -369,6 +370,23 @@
}
});

// reduce :: Foldable f => (a -> b -> a) -> a -> f b -> a
var reduce =
def('reduce',
{b: [Foldable]},
[$.Function, a, b, a],
function(f, initial, foldable) {
if (_type(foldable) === 'Array') {
var acc = initial;
for (var idx = 0; idx < foldable.length; idx += 1) {
acc = f(acc, foldable[idx]);
}
return acc;
} else {
return foldable.reduce(f, initial);
}
});

//. ### Classify

//# type :: a -> String
Expand Down Expand Up @@ -688,7 +706,7 @@
{},
[$.Array($.Function), $.Function],
function(fs) {
var n = 1 + R.sum(R.map(R.length, fs)) - fs.length;
var n = 1 + sum(R.map(R.length, fs)) - fs.length;
return R.curryN(n, function() {
var args = Array.prototype.slice.call(arguments);
for (var idx = 0; idx < fs.length; idx += 1) {
Expand Down Expand Up @@ -953,7 +971,7 @@
//. > S.Nothing().map(S.inc)
//. Nothing()
//.
//. > S.Just([1, 2, 3]).map(R.sum)
//. > S.Just([1, 2, 3]).map(S.sum)
//. Just(6)
//. ```
Maybe.prototype.map =
Expand Down Expand Up @@ -1236,7 +1254,7 @@
def('mapMaybe',
{},
[$.Function, $.Array(a), $.Array(b)],
meld([R.map, justs]));
function(f, xs) { return justs(R.map(f, xs)); });

//# encase :: (a -> b) -> a -> Maybe b
//.
Expand Down Expand Up @@ -1511,7 +1529,7 @@
//. > S.Left('Cannot divide by zero').map(S.inc)
//. Left('Cannot divide by zero')
//.
//. > S.Right([1, 2, 3]).map(R.sum)
//. > S.Right([1, 2, 3]).map(S.sum)
//. Right(6)
//. ```
Either.prototype.map =
Expand Down Expand Up @@ -2378,21 +2396,7 @@
//. > S.reduce((xs, x) => [x].concat(xs), [], [1, 2, 3, 4, 5])
//. [5, 4, 3, 2, 1]
//. ```
S.reduce =
def('reduce',
{b: [Foldable]},
[$.Function, a, b, a],
function(f, initial, foldable) {
if (_type(foldable) === 'Array') {
var acc = initial;
for (var idx = 0; idx < foldable.length; idx += 1) {
acc = f(acc, foldable[idx]);
}
return acc;
} else {
return foldable.reduce(f, initial);
}
});
S.reduce = reduce;

//# unfoldr :: (b -> Maybe (a, b)) -> b -> [a]
//.
Expand Down Expand Up @@ -2535,6 +2539,20 @@
[$.FiniteNumber, $.FiniteNumber, $.FiniteNumber],
function(a, b) { return a + b; });

//# sum :: Foldable f => f FiniteNumber -> FiniteNumber
//.
//. Returns the sum of the given array of (finite) numbers.
//.
//. ```javascript
//. > S.sum([1, 2, 3, 4, 5])
//. 15
//. ```
var sum = S.sum =
def('sum',
{f: [Foldable]},
[f, $.FiniteNumber],
reduce(function(a, b) { return a + b; }, 0));

//# sub :: FiniteNumber -> FiniteNumber -> FiniteNumber
//.
//. Returns the difference between two (finite) numbers.
Expand Down Expand Up @@ -2585,12 +2603,26 @@
//. > S.mult(4, 2)
//. 8
//. ```
S.mult =
var mult = S.mult =
def('mult',
{},
[$.FiniteNumber, $.FiniteNumber, $.FiniteNumber],
function(a, b) { return a * b; });

//# product :: Foldable f => f FiniteNumber -> FiniteNumber
//.
//. Returns the product of the given array of (finite) numbers.
//.
//. ```javascript
//. > S.product([1, 2, 3, 4, 5])
//. 120
//. ```
S.product =
def('product',
{f: [Foldable]},
[f, $.FiniteNumber],
reduce(function(a, b) { return a * b; }, 1));

//# div :: FiniteNumber -> NonZeroFiniteNumber -> FiniteNumber
//.
//. Returns the result of dividing its first argument (a finite number) by
Expand Down
85 changes: 85 additions & 0 deletions test/product.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict';

var throws = require('assert').throws;

var eq = require('./utils').eq;
var errorEq = require('./utils').errorEq;
var S = require('..');


describe('product', function() {

it('is a unary function', function() {
eq(typeof S.product, 'function');
eq(S.product.length, 1);
});

it('type checks its arguments', function() {
throws(function() { S.product('xxx'); },
errorEq(TypeError,
'Type-class constraint violation\n' +
'\n' +
'product :: Foldable f => f -> FiniteNumber\n' +
' ^^^^^^^^^^ ^\n' +
' 1\n' +
'\n' +
'1) "xxx" :: String\n' +
'\n' +
'‘product’ requires ‘f’ to satisfy the Foldable type-class constraint; the value at position 1 does not.\n'));

throws(function() { S.product([1, 'xxx']); },
errorEq(TypeError,
'Type-variable constraint violation\n' +
'\n' +
'product :: Foldable f => f -> FiniteNumber\n' +
' ^\n' +
' 1\n' +
'\n' +
'1) [1, "xxx"] :: Array ???\n' +
'\n' +
'Since there is no type of which all the above values are members, the type-variable constraint has been violated.\n'));

throws(function() { S.product([1, Infinity]); },
errorEq(TypeError,
'Invalid value\n' +
'\n' +
'product :: Foldable f => f -> FiniteNumber\n' +
' ^^^^^^^^^^^^\n' +
' 1\n' +
'\n' +
'1) Infinity :: Number, ValidNumber\n' +
'\n' +
'The value at position 1 is not a member of ‘FiniteNumber’.\n'));

throws(function() { S.product([1, -Infinity]); },
errorEq(TypeError,
'Invalid value\n' +
'\n' +
'product :: Foldable f => f -> FiniteNumber\n' +
' ^^^^^^^^^^^^\n' +
' 1\n' +
'\n' +
'1) -Infinity :: Number, ValidNumber\n' +
'\n' +
'The value at position 1 is not a member of ‘FiniteNumber’.\n'));
});

it('returns the product of an array of numbers', function() {
eq(S.product([]), 1);
eq(S.product([0, 1, 2, 3]), 0);
eq(S.product([-0, 1, 2, 3]), -0);
eq(S.product([1, 2, 3, 4, 5]), 120);
eq(S.product([1, 2, 3, 4, -5]), -120);
});

it('can be applied to Maybes', function() {
eq(S.product(S.Nothing()), 1);
eq(S.product(S.Just(42)), 42);
});

it.skip('can be applied to Eithers', function() {
eq(S.product(S.Left('xxx')), 1);
eq(S.product(S.Right(42)), 42);
});

});
85 changes: 85 additions & 0 deletions test/sum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
'use strict';

var throws = require('assert').throws;

var eq = require('./utils').eq;
var errorEq = require('./utils').errorEq;
var S = require('..');


describe('sum', function() {

it('is a unary function', function() {
eq(typeof S.sum, 'function');
eq(S.sum.length, 1);
});

it('type checks its arguments', function() {
throws(function() { S.sum('xxx'); },
errorEq(TypeError,
'Type-class constraint violation\n' +
'\n' +
'sum :: Foldable f => f -> FiniteNumber\n' +
' ^^^^^^^^^^ ^\n' +
' 1\n' +
'\n' +
'1) "xxx" :: String\n' +
'\n' +
'‘sum’ requires ‘f’ to satisfy the Foldable type-class constraint; the value at position 1 does not.\n'));

throws(function() { S.sum([1, 'xxx']); },
errorEq(TypeError,
'Type-variable constraint violation\n' +
'\n' +
'sum :: Foldable f => f -> FiniteNumber\n' +
' ^\n' +
' 1\n' +
'\n' +
'1) [1, "xxx"] :: Array ???\n' +
'\n' +
'Since there is no type of which all the above values are members, the type-variable constraint has been violated.\n'));

throws(function() { S.sum([1, Infinity]); },
errorEq(TypeError,
'Invalid value\n' +
'\n' +
'sum :: Foldable f => f -> FiniteNumber\n' +
' ^^^^^^^^^^^^\n' +
' 1\n' +
'\n' +
'1) Infinity :: Number, ValidNumber\n' +
'\n' +
'The value at position 1 is not a member of ‘FiniteNumber’.\n'));

throws(function() { S.sum([1, -Infinity]); },
errorEq(TypeError,
'Invalid value\n' +
'\n' +
'sum :: Foldable f => f -> FiniteNumber\n' +
' ^^^^^^^^^^^^\n' +
' 1\n' +
'\n' +
'1) -Infinity :: Number, ValidNumber\n' +
'\n' +
'The value at position 1 is not a member of ‘FiniteNumber’.\n'));
});

it('sums an array of numbers together', function() {
eq(S.sum([]), 0);
eq(S.sum([0, 1, 2, 3]), 6);
eq(S.sum([-0, 1, 2, 3]), 6);
eq(S.sum([1, 2, 3, 4, 5]), 15);
eq(S.sum([1, 2, 3, 4, -5]), 5);
});

it('can be applied to Maybes', function() {
eq(S.sum(S.Nothing()), 0);
eq(S.sum(S.Just(42)), 42);
});

it.skip('can be applied to Eithers', function() {
eq(S.sum(S.Left('xxx')), 0);
eq(S.sum(S.Right(42)), 42);
});

});

0 comments on commit d26bc4e

Please sign in to comment.