Skip to content

Commit

Permalink
array: generalize S.head, S.last, S.tail, and S.init
Browse files Browse the repository at this point in the history
  • Loading branch information
syves committed Mar 29, 2019
1 parent 7ac726d commit e6485da
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 35 deletions.
114 changes: 87 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3063,81 +3063,136 @@
impl: at
};

//# head :: Array a -> Maybe a
//# head :: Foldable f => f a -> Maybe a
//.
//. Returns Just the first element of the given array if the array contains
//. at least one element; Nothing otherwise.
//. Returns Just the first element of the given structure if the structure
//. contains at least one element; Nothing otherwise.
//.
//. ```javascript
//. > S.head ([1, 2, 3])
//. Just (1)
//.
//. > S.head ([])
//. Nothing
//.
//. > S.head (Cons (1) (Cons (2) (Cons (3) (Nil))))
//. Just (1)
//.
//. > S.head (Nil)
//. Nothing
//. ```
function head(foldable) {
// Fast path for arrays.
if (Array.isArray (foldable)) {
return foldable.length > 0 ? Just (foldable[0]) : Nothing;
}
return Z.reduce (function(m, x) { return m.isJust ? m : Just (x); },
Nothing,
foldable);
}
_.head = {
consts: {},
types: [$.Array (a), $Maybe (a)],
impl: array (Nothing) (B (K) (Just))
consts: {f: [Z.Foldable]},
types: [f (a), $Maybe (a)],
impl: head
};

//# last :: Array a -> Maybe a
//# last :: Foldable f => f a -> Maybe a
//.
//. Returns Just the last element of the given array if the array contains
//. at least one element; Nothing otherwise.
//. Returns Just the last element of the given structure if the structure
//. contains at least one element; Nothing otherwise.
//.
//. ```javascript
//. > S.last ([1, 2, 3])
//. Just (3)
//.
//. > S.last ([])
//. Nothing
//.
//. > S.last (Cons (1) (Cons (2) (Cons (3) (Nil))))
//. Just (3)
//.
//. > S.last (Nil)
//. Nothing
//. ```
function last(xs) {
return xs.length > 0 ? Just (xs[xs.length - 1]) : Nothing;
function last(foldable) {
// Fast path for arrays.
if (Array.isArray (foldable)) {
return foldable.length > 0 ? Just (foldable[foldable.length - 1])
: Nothing;
}
return Z.reduce (function(_, x) { return Just (x); }, Nothing, foldable);
}
_.last = {
consts: {},
types: [$.Array (a), $Maybe (a)],
consts: {f: [Z.Foldable]},
types: [f (a), $Maybe (a)],
impl: last
};

//# tail :: Array a -> Maybe (Array a)
//# tail :: (Applicative f, Foldable f, Monoid (f a)) => f a -> Maybe (f a)
//.
//. Returns Just all but the first of the given array's elements if the
//. array contains at least one element; Nothing otherwise.
//. Returns Just all but the first of the given structure's elements if the
//. structure contains at least one element; Nothing otherwise.
//.
//. ```javascript
//. > S.tail ([1, 2, 3])
//. Just ([2, 3])
//.
//. > S.tail ([])
//. Nothing
//.
//. > S.tail (Cons (1) (Cons (2) (Cons (3) (Nil))))
//. Just (Cons (2) (Cons (3) (Nil)))
//
//. > S.tail (Nil)
//. Nothing
//. ```
function tail(foldable) {
// Fast path for arrays.
if (Array.isArray (foldable)) {
return foldable.length > 0 ? Just (foldable.slice (1)) : Nothing;
}
var empty = Z.empty (foldable.constructor);
return Z.reduce (function(m, x) {
return Just (maybe (empty) (append (x)) (m));
}, Nothing, foldable);
}
_.tail = {
consts: {},
types: [$.Array (a), $Maybe ($.Array (a))],
impl: array (Nothing) (K (Just))
consts: {f: [Z.Applicative, Z.Foldable, Z.Monoid]},
types: [f (a), $Maybe (f (a))],
impl: tail
};

//# init :: Array a -> Maybe (Array a)
//# init :: (Applicative f, Foldable f, Monoid (f a)) => f a -> Maybe (f a)
//.
//. Returns Just all but the last of the given array's elements if the
//. array contains at least one element; Nothing otherwise.
//. Returns Just all but the last of the given structure's elements if the
//. structure contains at least one element; Nothing otherwise.
//.
//. ```javascript
//. > S.init ([1, 2, 3])
//. Just ([1, 2])
//.
//. > S.init ([])
//. Nothing
//.
//. > S.init (Cons (1) (Cons (2) (Cons (3) (Nil))))
//. Just (Cons (1) (Cons (2) (Nil)))
//.
//. > S.init (Nil)
//. Nothing
//. ```
function init(xs) {
return xs.length > 0 ? Just (xs.slice (0, -1)) : Nothing;
function init(foldable) {
// Fast path for arrays.
if (Array.isArray (foldable)) {
return foldable.length > 0 ? Just (foldable.slice (0, -1)) : Nothing;
}
var empty = Z.empty (foldable.constructor);
return Z.map (Pair.snd, Z.reduce (function(m, x) {
return Just (Pair (x) (maybe (empty) (pair (append)) (m)));
}, Nothing, foldable));
}
_.init = {
consts: {},
types: [$.Array (a), $Maybe ($.Array (a))],
consts: {f: [Z.Applicative, Z.Foldable, Z.Monoid]},
types: [f (a), $Maybe (f (a))],
impl: init
};

Expand Down Expand Up @@ -3379,10 +3434,15 @@
//. > S.append ([3]) (S.Just ([1, 2]))
//. Just ([1, 2, 3])
//. ```
function append(x) {
return function(xs) {
return Z.append (x, xs);
};
}
_.append = {
consts: {f: [Z.Applicative, Z.Semigroup]},
types: [a, f (a), f (a)],
impl: curry2 (Z.append)
impl: append
};

//# prepend :: (Applicative f, Semigroup (f a)) => a -> f a -> f a
Expand Down
10 changes: 8 additions & 2 deletions test/head.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
'use strict';

const S = require ('..');
const S = require ('./internal/sanctuary');

const {Nil, Cons} = require ('./internal/List');
const eq = require ('./internal/eq');


test ('head', () => {

eq (typeof S.head) ('function');
eq (S.head.length) (1);
eq (S.show (S.head)) ('head :: Array a -> Maybe a');
eq (S.show (S.head)) ('head :: Foldable f => f a -> Maybe a');

eq (S.head ([])) (S.Nothing);
eq (S.head (['foo'])) (S.Just ('foo'));
eq (S.head (['foo', 'bar'])) (S.Just ('foo'));
eq (S.head (['foo', 'bar', 'baz'])) (S.Just ('foo'));

eq (S.head (Nil)) (S.Nothing);
eq (S.head (Cons ('foo') (Nil))) (S.Just ('foo'));
eq (S.head (Cons ('foo') (Cons ('bar') (Nil)))) (S.Just ('foo'));
eq (S.head (Cons ('foo') (Cons ('bar') (Cons ('baz') (Nil))))) (S.Just ('foo'));

});
10 changes: 8 additions & 2 deletions test/init.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
'use strict';

const S = require ('..');
const S = require ('./internal/sanctuary');

const {Nil, Cons} = require ('./internal/List');
const eq = require ('./internal/eq');


test ('init', () => {

eq (typeof S.init) ('function');
eq (S.init.length) (1);
eq (S.show (S.init)) ('init :: Array a -> Maybe (Array a)');
eq (S.show (S.init)) ('init :: (Applicative f, Foldable f, Monoid f) => f a -> Maybe (f a)');

eq (S.init ([])) (S.Nothing);
eq (S.init (['foo'])) (S.Just ([]));
eq (S.init (['foo', 'bar'])) (S.Just (['foo']));
eq (S.init (['foo', 'bar', 'baz'])) (S.Just (['foo', 'bar']));

eq (S.init (Nil)) (S.Nothing);
eq (S.init (Cons ('foo') (Nil))) (S.Just (Nil));
eq (S.init (Cons ('foo') (Cons ('bar') (Nil)))) (S.Just (Cons ('foo') (Nil)));
eq (S.init (Cons ('foo') (Cons ('bar') (Cons ('baz') (Nil))))) (S.Just (Cons ('foo') (Cons ('bar') (Nil))));

});
10 changes: 8 additions & 2 deletions test/last.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
'use strict';

const S = require ('..');
const S = require ('./internal/sanctuary');

const {Nil, Cons} = require ('./internal/List');
const eq = require ('./internal/eq');


test ('last', () => {

eq (typeof S.last) ('function');
eq (S.last.length) (1);
eq (S.show (S.last)) ('last :: Array a -> Maybe a');
eq (S.show (S.last)) ('last :: Foldable f => f a -> Maybe a');

eq (S.last ([])) (S.Nothing);
eq (S.last (['foo'])) (S.Just ('foo'));
eq (S.last (['foo', 'bar'])) (S.Just ('bar'));
eq (S.last (['foo', 'bar', 'baz'])) (S.Just ('baz'));

eq (S.last (Nil)) (S.Nothing);
eq (S.last (Cons ('foo') (Nil))) (S.Just ('foo'));
eq (S.last (Cons ('foo') (Cons ('bar') (Nil)))) (S.Just ('bar'));
eq (S.last (Cons ('foo') (Cons ('bar') (Cons ('baz') (Nil))))) (S.Just ('baz'));

});
10 changes: 8 additions & 2 deletions test/tail.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
'use strict';

const S = require ('..');
const S = require ('./internal/sanctuary');

const {Nil, Cons} = require ('./internal/List');
const eq = require ('./internal/eq');


test ('tail', () => {

eq (typeof S.tail) ('function');
eq (S.tail.length) (1);
eq (S.show (S.tail)) ('tail :: Array a -> Maybe (Array a)');
eq (S.show (S.tail)) ('tail :: (Applicative f, Foldable f, Monoid f) => f a -> Maybe (f a)');

eq (S.tail ([])) (S.Nothing);
eq (S.tail (['foo'])) (S.Just ([]));
eq (S.tail (['foo', 'bar'])) (S.Just (['bar']));
eq (S.tail (['foo', 'bar', 'baz'])) (S.Just (['bar', 'baz']));

eq (S.tail (Nil)) (S.Nothing);
eq (S.tail (Cons ('foo') (Nil))) (S.Just (Nil));
eq (S.tail (Cons ('foo') (Cons ('bar') (Nil)))) (S.Just (Cons ('bar') (Nil)));
eq (S.tail (Cons ('foo') (Cons ('bar') (Cons ('baz') (Nil))))) (S.Just (Cons ('bar') (Cons ('baz') (Nil))));

});

0 comments on commit e6485da

Please sign in to comment.