1

chain method

A value which has a Chain must provide a chain method. The chain method takes one argument:

m.chain(f)

  1. f must be a function which returns a value
    • If f is not a function, the behaviour of chain is unspecified.
    • f must return a value of the same Chain
  2. chain must return a value of the same Chain

GitHub - fantasyland

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?

1 Answers1

0

In your second example you should use .map():

a.chain(x => b.map(y => sub(x)(y))); 

and then everything follows the rules.

For comparison here are the equivalent Haskell signatures:

fmap  :: m a -> (a ->   b)   -> m b    -- same as .map()
(>>=) :: m a -> (a -> m b)   -> m b    -- same as .chain()

So if your function returns a monadic value, use .chain(). Otherwise use .map().

ErikR
  • 51,541
  • 9
  • 73
  • 124
  • The problem is when someone misuses this option implementation in a way I did in my second example, it violates the spec. Thus my implementation might not be compliant with fantasy land. The only way to prevent this seems to consists in adding a type check at run-time. I believe that this is kind of ugly. –  Jun 11 '16 at 19:27
  • Your implementation of an Option monad is compatible with the spec. Your use of `.chain()` is not. Users of your Chainable are expected to use the appropriate method - either `.chain()` or `.map()` depending on what function they are supplying. – ErikR Jun 11 '16 at 19:59
  • OK, I am not fully convinced but accept the answer, thanks! –  Jun 12 '16 at 18:52
  • I think it totally makes sense. Users also have a responsability. You can go buy a car and do an accident 10 minutes later .. Or you can buy a car and use it carefully for 20 years. – Arnaud Boeglin Oct 28 '17 at 08:49