const assert = require('assert');
class Maybe {
static of(x) {
return new Just(x);
}
}
class Nothing extends Maybe {
inspect() {
return `Nothing`;
}
map(f) {
return new Nothing();
}
ap(f) {
return new Nothing();
}
reduce(f, z) {
return z;
}
traverse(f, of) {
throw new Error("not implemented yet");
}
traverse1(f) {
throw new Error("cannot implement");
}
}
class Just extends Maybe {
constructor(x) {
super();
this.x = x;
}
inspect() {
return `Just(${typeof this.x.inspect === 'function' ? this.x.inspect() : this.x})`;
}
map(f) {
return new Just(f(this.x));
}
ap(f) {
return new Just(f.x(this.x));
}
reduce(f, z) {
return f(z, this.x);
}
traverse(f, of) {
throw new Error("not implemented yet");
}
traverse1(f) {
return f(this.x).map(x => new Just(x));
}
}
class Identity {
constructor(x) {
this.x = x;
}
inspect() {
return `Identity(${typeof this.x.inspect === 'function' ? this.x.inspect() : this.x})`;
}
map(f) {
return new Identity(f(this.x));
}
ap(f) {
return new Identity(f.x(this.x));
}
static of(x) {
return new Identity(x);
}
}
const x = new Nothing();
const y = new Just("hello");
const f = x => new Identity(x + " world");
const g = x => new Nothing();
const h = x => new Just(x + " world");
assert.equal(x.traverse(f, Identity.of).inspect(), `Identity(Nothing)`);
assert.equal(y.traverse(f, Identity.of).inspect(), `Identity(Just(hello world))`);
assert.equal(x.traverse(g, Maybe.of).inspect(), `Just(Nothing)`);
assert.equal(y.traverse(g, Maybe.of).inspect(), `Nothing`);
assert.equal(x.traverse(h, Maybe.of).inspect(), `Just(Nothing)`);
assert.equal(y.traverse(h, Maybe.of).inspect(), `Just(Just(hello world))`);
assert.throws(() => x.traverse1(f).inspect(), /^Error: cannot implement$/);
assert.equal(y.traverse1(f).inspect(), `Identity(Just(hello world))`);
assert.throws(() => x.traverse1(g).inspect(), /^Error: cannot implement$/);
assert.equal(y.traverse1(g).inspect(), `Nothing`);
assert.throws(() => x.traverse1(h).inspect(), /^Error: cannot implement$/);
assert.equal(y.traverse1(h).inspect(), `Just(Just(hello world))`);