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

I don't agree with your first remark: what do you expect to be returned when you return a value not wrapped in a promise?

The ambiguity is actually the auto unwrapping of promises for subsequent then calls but I've yet to see a case where it's a problem (it's actually what makes the API clean looking when used).

For your last snippet, I don't see an array so I don't know what ordering you want to preserve (p always returns the same value so r will always be the same), if it's actually inside a loop, you should use Promise.all or Promise.map.



I think the confusion stems from the fact that .then() acts like either .map() or .flatMap() depending on the return type.

If .then() was just .flatMap(), you could expect an error if you didn't return a Promise (for example, reject with a TypeError).

If .then() was just .map(), you could return a value of any type (for example, string or Promise<number>) and get back a Promise for a value of that type (Promise<string> or Promise<Promise<number>>).


Actually, it's slightly more complex because if the result of the function passed to then were somehow a Promise<Promise<something>>, it would be flattened recursively so that the ultimate result is just Promise<something>. (Of course, due to .then's flattening semantics it's highly unlikely you'd have a Promise<Promise<...>> in the first places).

The distinction between map and flatMap makes sense for, say, lists, because you often do want to keep the non-flattened structure around. From promises, I can't think of a reason why that would be desired. You just want to represent the final result after all necessary waiting is completed. I suppose a library could provide stricter map and flatMap for people who deeply care about this.


what do you expect to be returned when you return a value not wrapped in a promise?

Nothing that's a type error, it should throw a runtime exception just like all type errors in javascript, and it will show up in the debugger for the programmer to fix.

The ambiguity is actually the auto unwrapping of promises for subsequent then calls but I've yet to see a case where it's a problem (it's actually what makes the API clean looking when used).

I'm not sure I quite understand what you're referring to, but I think you mean the fact that you can chain then calls

    Promise.resolve(5)
      .then(x => Promise.resolve(x + 5))
      .then(x => Promise.resolve(x + 5))
      .then(x => {
        // x == 15
      });
This is totally sound behavior. The simplified signature of `.then()` is

    then :: Promise a -> (a -> Promise b) -> Promise b
This makes `.then()` the `bind` function for the Promise monad. As a refresher for people who don't think about monads much:

    (>>=) :: Monad m => m a -> (a -> m b) -> m b
While on this tangent, I'll note that `Promise.resolve()` is the `return` function for the promise monad:

    return :: Monad m => a -> m a
    Promise.resolve :: a -> Promise a
For your last snippet, I don't see an array so I don't know what ordering you want to preserve

It's a contrived example demonstrating how switching based on whether the return value is a promise can make hard to detect bugs. It's hard to come up with a short example for this because strange values turning up where they don't belong is a property of large, dynamically typed systems. So yes, the example doesn't use all the best practices.


Well I can't really see any benefit to force the wrapping all returned values in promises without a proper example.

The then() signature can be expressed in typescript and I suppose in alternatives like flow (using the union type): https://github.com/borisyankov/DefinitelyTyped/blob/master/e... .


Sure, you can represent it that way, but it needlessly complicates the semantics of `then`. If they were merely monadic bind then it'd easily be 1/20th as confusing.

The "overload everything every which way and assume that people are really sensitive to the differences" strategy just begs misunderstanding.

Monadic bind as the core and basis of your API is fantastic. It is exactly enough and no more than you need to define effectful sequencing semantics and, if you like, you can build everything atop it. And, honestly, you do anyway. But right now the design obscures the simple basis technology in order to have heaps of overload magic.


He's stating the problem is dynamic typing... so the expectation would be a type error (!).




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

Search: