Skip to content

Instantly share code, notes, and snippets.

@buzzdecafe
Last active April 23, 2020 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save buzzdecafe/727e115150bfe742f4467c07ceb01ab1 to your computer and use it in GitHub Desktop.
Save buzzdecafe/727e115150bfe742f4467c07ceb01ab1 to your computer and use it in GitHub Desktop.
Union types are beautiful
import Type from 'union-type';
// `tail` function must be return a Stream type, but no way to enforce that without evaluating it :-(
const Stream = Type({
Empty: [],
Cons: [() => true, Function]
});
Stream.prototype.chain = function(f) {
return Stream.case({
Empty: () => Stream.Empty,
Cons: (head, tail) => f(head).concat(tail().chain(f))
}, this);
};
Stream.prototype.concat = function(s) {
return Stream.case({
Empty: () => s,
Cons: (head, tail) => Stream.Cons(head, () => tail().concat(s))
}, this);
};
Stream.prototype.drop = function(n) {
return Stream.case({
Empty: Stream.empty,
Cons: (head, tail) => n < 1 ? Stream.Cons(head, tail) :
n === 1 ? tail()
: tail().drop(n - 1)
}, this);
};
Stream.prototype.filter = function(p) {
return Stream.case({
Empty: Stream.empty,
Cons: (head, tail) => p(head) ? Stream.Cons(head, () => tail().filter(p))
: tail().filter(p)
}, this);
};
Stream.prototype.head = function() {
return Stream.case({
Empty: () => Stream.Empty,
Cons: (head, _) => head
}, this);
};
Stream.prototype.isEmpty = function() {
return Stream.case({
Empty: () => true,
Cons: () => false
}, this);
};
Stream.prototype.isStream = () => true;
Stream.prototype.map = function(f) {
return Stream.case({
Empty: () => Stream.Empty,
Cons: (head, tail) => Stream.Cons(f(head), () => tail().map(f))
}, this);
};
Stream.prototype.tail = function() {
return Stream.case({
Empty: () => Stream.Empty,
Cons: (_, tail) => tail()
}, this);
};
Stream.prototype.take = function(n) {
return Stream.case({
Empty: Stream.empty,
Cons: (head, tail) => n < 1 ? Stream.Empty
: Stream.Cons(head, () => tail().take(n - 1))
}, this);
};
Stream.prototype.toArray = function() {
return Stream.case({
Empty: () => [],
Cons: (head, tail) => [head].concat(tail().toArray())
}, this);
};
Stream.prototype.toString = function() {
return Stream.case({
Empty: () => 'Empty',
Cons: (head, tail) => `Cons(${head}, ${tail().toString()})`
}, this);
};
Stream.empty = () => Stream.Empty;
Stream.fromArray = xs => xs.reduceRight((acc, x) => Stream.Cons(x, () => acc), Stream.Empty);
Stream.isStream = s => s && s.isStream === Stream.prototype.isStream && s.isStream(); // force to Bool
Stream.of = x => Stream.Cons(x, () => Stream.Empty);
export default Stream;
// create an infinite stream of natural numbers
const nats = from => Stream.Cons(from, () => nats(from + 1));
nats(1).head(); //=> 1
nats(1).tail().head(); //=> 2
nats(1).tail().tail().head(); //=> 3
nats(1).tail().tail().tail().head(); //=> 4
// etc.
// alternately
nats(1).drop(3).head(); //=> 4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment