Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Let me give it a shot. Monads as in category theory are a bit more abstract than this explanation, but for the purposes of programming you can think like this:

A functor has a `map` mechanism, which takes a value A and a function B, and returns something that has the same type as A. A simplistic example of a "mappable" that isn't a functor is `const map = setTimeout`: it technically take a value A (the time) and a function B, but `map(fn1, map(fn2, 1))` is semantically nonsensical even though all the types technically align and the code runs (because the effective "type" of the return value does not use milliseconds as a unit of measurement, and effectively causes undefined behavior).

A monad, in addition to being a functor, must also not have extra funky semantics. A example of a functor that isn't a monad is `Promise`. Imagine we did this:

    TimeSequence = Promise
    TimeSequence.of = TimeSequence.resolve
    TimeSequence.prototype.map = TimeSequence.prototype.then
    t1 = TimeSequence.of(1) // t1 has type TimeSequence<number>
    t2 = t1.map(v => v * 2) // t2 has type TimeSequence<number>
    // BUT
    t3 = t2.map(v => TimeSequence.of(v)) // t3 has type TimeSequence<number>, NOT TimeSequence<TimeSequence<number>>
The Array equivalent is:

    SpaceSequence = Array
    a1 = Array.of(1) // a1 has type SpaceSequence<number>
    a2 = a1.map(v => v * 2) // a2 has type SpaceSequence<number>
    a3 = a2.map(v => Array.of(v)) // a3 has type SpaceSequence<SpaceSequence<number>>
    a4 = a3.flat() // this is a separate operation to get a SpaceSequence<number> out of a3
Here, Array is "monadic" because it has both map and flatMap as separate operations. Promise conflates the two, hence it's not monadic.

(Note: I say Array is "monadic" in quotes, because this is only true if we constrain its uses to patterns that conform to monadic laws. If you do `array.map(parseInt)`, that's valid JS, but as far as monads go, all bets are off!)

Also, note that I renamed a bunch of methods (resolve => of, then => map) and called things `Sequence`s. This is the value of functional orientation as it pertains to programming: when we say we've implemented monadic TimeSequence and SpaceSequence, in practical terms that means that I can write code for one type and reuse it for the other type because both follow the same semantics within one larger, common context. It's a lot easier to say "these implementations are not monadic" than it is to say "Promise-based logic is not compatible with Array-based logic because the method names are all different and the semantics of flattening Promises of Promises don't match the semantics of Arrays of Arrays (or Streams of Streams, etc)".

If this is still too abstract, here's an application: suppose I'm writing a task runner that takes tasks and runs them in parallel (but queues once we reach a parallelization limit). I can serialize this representation in space by turning a array of arrays into JSON. But I can also represent this as a grid of timings (meaning, for example, I can write shared code to iterate over both representations, rather than hard-coding different implementations for each type of grid)

Now, you could make an argument that in isolation and within some artificial constrainst, both Array and Promise are monadic (i.e. Promise<Array<number>> works just fine), but saying that's monadic is basically pedantic wankery, since for practical purposes, you can't arbitrarily write code against a specification to abstractly treat promises and arrays as an implementation of a common abstraction (e.g. a sequence of some sort)



> A monad, in addition to being a functor, must also not have extra funky semantics. A example of a functor that isn't a monad is `Promise`.

Not quite. You've come up with a great example of a problem with JavaScript Promise, but the problem is actually that it isn't a functor (it violates the composition law, for example).


Hmm, fair point.

I couldn't think of another popular construct in JS that is a proper functor. Do you have any better suggestions?


Any of the standard functor-but-not-monad examples, e.g. multidimensional arrays (where you can implement map but can't implement map2 or flatMap, because the dimensions won't necessarily line up)?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: