From bfc14cae6ce6fe22d09e987e9adf433912cd8851 Mon Sep 17 00:00:00 2001 From: David Chambers Date: Wed, 3 Feb 2016 21:58:47 -0800 Subject: [PATCH] number, string: add S.min and S.max --- index.js | 48 +++++++++++++++++++++++++++++++++ test/index.js | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) diff --git a/index.js b/index.js index 0d648c7f..3c10e4bf 100644 --- a/index.js +++ b/index.js @@ -169,6 +169,16 @@ } ); + // Ord :: TypeClass + var Ord = $.TypeClass( + 'sanctuary/Ord', + function(x) { + var type = _type(x); + return type === 'Number' && $.ValidNumber.test(x) || + type === 'String'; + } + ); + // Semigroup :: TypeClass var Semigroup = $.TypeClass( 'sanctuary/Semigroup', @@ -2347,6 +2357,44 @@ [$.FiniteNumber, $.NonZeroFiniteNumber, $.FiniteNumber], function(a, b) { return a / b; }); + //# min :: Ord a => a -> a -> a + //. + //. Returns the smaller of its two arguments. + //. + //. See also [`max`](#max). + //. + //. ```javascript + //. > S.min(42, 99) + //. 42 + //. + //. > S.min('xyz', 'abc') + //. 'abc' + //. ``` + S.min = + def('min', + {a: [Ord]}, + [a, a, a], + function(x, y) { return x < y ? x : y; }); + + //# max :: Ord a => a -> a -> a + //. + //. Returns the larger of its two arguments. + //. + //. See also [`min`](#min). + //. + //. ```javascript + //. > S.max(42, 99) + //. 99 + //. + //. > S.max('xyz', 'abc') + //. 'xyz' + //. ``` + S.max = + def('max', + {a: [Ord]}, + [a, a, a], + function(x, y) { return x > y ? x : y; }); + //. ### Integer //# even :: Integer -> Boolean diff --git a/test/index.js b/test/index.js index 53c510a3..a6312d54 100644 --- a/test/index.js +++ b/test/index.js @@ -3575,6 +3575,80 @@ describe('number', function() { }); + describe('min', function() { + + it('is a binary function', function() { + eq(typeof S.min, 'function'); + eq(S.min.length, 2); + }); + + it('type checks its arguments', function() { + assert.throws(function() { S.min(/x/); }, + errorEq(TypeError, + '‘min’ requires ‘a’ to implement Ord; ' + + 'RegExp does not')); + + assert.throws(function() { S.min(NaN); }, + errorEq(TypeError, + '‘min’ requires ‘a’ to implement Ord; ' + + 'Number does not')); + }); + + it('may be applied to (valid) numbers', function() { + eq(S.min(42, 99), 42); + eq(S.min(0.1, 0.01), 0.01); + eq(S.min(Infinity, -Infinity), -Infinity); + }); + + it('may be applied to strings', function() { + eq(S.min('abc', 'xyz'), 'abc'); + eq(S.min('xyz', 'abc'), 'abc'); + }); + + it('is curried', function() { + eq(S.min(42).length, 1); + eq(S.min(42)(99), 42); + }); + + }); + + describe('max', function() { + + it('is a binary function', function() { + eq(typeof S.max, 'function'); + eq(S.max.length, 2); + }); + + it('type checks its arguments', function() { + assert.throws(function() { S.max(/x/); }, + errorEq(TypeError, + '‘max’ requires ‘a’ to implement Ord; ' + + 'RegExp does not')); + + assert.throws(function() { S.max(NaN); }, + errorEq(TypeError, + '‘max’ requires ‘a’ to implement Ord; ' + + 'Number does not')); + }); + + it('may be applied to (valid) numbers', function() { + eq(S.max(42, 99), 99); + eq(S.max(0.1, 0.01), 0.1); + eq(S.max(Infinity, -Infinity), Infinity); + }); + + it('may be applied to strings', function() { + eq(S.max('abc', 'xyz'), 'xyz'); + eq(S.max('xyz', 'abc'), 'xyz'); + }); + + it('is curried', function() { + eq(S.max(42).length, 1); + eq(S.max(42)(99), 99); + }); + + }); + }); describe('integer', function() {