chain
methodA value which has a Chain must provide a
chain
method. The chain method takes one argument:
m.chain(f)
f
must be a function which returns a value
- If
f
is not a function, the behaviour ofchain
is unspecified.f
must return a value of the same Chainchain
must return a value of the same Chain
Given is a simple implementation of the option monad:
// prototypes:
const someProto = {
of(x) { return some(x) },
map(f) { return some(f(this.x)) },
ap(ftor) { return ftor.map(this.x) },
join() { return this.x },
chain(mf) { return this.map(mf).join() }
};
const noneProto = {
of() { return this },
map() { return this },
ap() { return this },
join() { return this },
chain() { return this }
};
// factories:
function some(x) {
return Object.assign(Object.create(someProto), {x: x});
}
function none() {
return Object.assign(Object.create(noneProto), {x: null});
}
To guarantee that chain
always returns an option monad, I'd have to ensure that mf
(monadic function) always returns one. This isn't possible, because mf
is not part of the implementation. Rather it's defined when the monad is used:
// auxiliary function:
const sub = y => x => x - y;
let a = some(2);
let b = some(3);
a.chain(x => b.chain(y => some(sub(x)(y)))); // {x: 1}
a.chain(x => b.chain(y => sub(x)(y))); // 1 - ouch!
In the second method application the passed function returns no monad, which leads to an unwrapped result of the monadic computation. I could add a type check to chain
or join
maybe by duck typing, to solve the issue - that would be pretty ugly though.
Why does the specification require type safety exactly at this point? Javascript is dynamically typed and I would prefer to write appropriate unit tests instead of performing type checks at run-time. Would I violate the specification then?
In your second example you should use .map():
and then everything follows the rules.
For comparison here are the equivalent Haskell signatures:
So if your function returns a monadic value, use
.chain()
. Otherwise use.map()
.