Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

array: generalize S.head, S.last, S.tail, and S.init #610

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
davidchambers marked this conversation as resolved.
Show resolved Hide resolved
//.
//. ```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.
davidchambers marked this conversation as resolved.
Show resolved Hide resolved
//.
//. ```javascript
//. > S.tail ([1, 2, 3])
//. Just ([2, 3])
//.
//. > S.tail ([])
//. Nothing
//.
davidchambers marked this conversation as resolved.
Show resolved Hide resolved
//. > 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);
}
davidchambers marked this conversation as resolved.
Show resolved Hide resolved
_.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);
davidchambers marked this conversation as resolved.
Show resolved Hide resolved
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))));

});
davidchambers marked this conversation as resolved.
Show resolved Hide resolved