Hacker News new | ask | show | jobs
by andrus 4431 days ago
The Promises/A+ assumption that you'd never want a Promise to return a Promise is really shortsighted.
2 comments

Promises are really Monads for asynchronous results.

A Monad kind of wraps a certain value. For example, we might have a Promise JSON from an API call. The bind method of a Monad (>>= in Haskell) takes a JSON Promise, and a function (JSON -> Promise b for some b), and returns a value of type Promise b. Promise.then is equivalent to bind.

In Haskell you might define them like this:

    data Promise msg val = Resolve val | Reject msg deriving Show

    instance Monad (Promise msg) where
        return = Resolve

        (>>=) :: Promise msg val -> (val -> Promise msg b) -> Promise msg b
        (Reject msg) >>= _ = Reject msg
        (Resolve val) >>= f = f val

They would be used like this

    newPromise = jsonPromise >>= transformJSON
In JavaScript, you would write

    var newPromise = jsonPromise.then(transformJSON)
However, the JavaScript case is slightly different. It is not as strict about types. It is possible (and typical) for transformJSON to return a bare type, rather than a Promise, while Haskell would require a Promise be returned every time. It treats an unwrapped value the same as an already fulfilled promise for that value. But automatically unboxing the promises that are returned gives them the full power of Monads.
No, "automatically unboxing the promises that are returned" does not give Promises "the full power of Monads", because you cannot represent a Promise for a Promise for a value. Promises/A+ breaks parametricity.
Promises are an ad hoc, informally-specified, bug-ridden, slow implementation of half of a monad.
I get a _lot_ of mileage out of being able to chain promises like that. If I really need to resolve a promise to a promise, I can resolve to an object wrapping the promise and then unwrap it on the receiving side.