Tuesday, April 2, 2013

Thinking in Promises

This morning I had an epiphany: of course some people find promises to be "easy" and "intuitive" at the same time while others find them "hard" and "unwieldy":

Programming in promises requires thinking about type signatures.

Depending on your perspective, this is either a great thing or The Worst Thing EverTM

People who have programmed in statically typed languages before are used to thinking about parameter types and return value types. For these people, it is easier to reason about a function from a URL to a Response object in terms of (URL) => Response rather than (URL, (Error, Response) => void) => void (using jsig notation). The former clearly expresses the expected input and output of the function, while the later only has input which is presumably being evaluated for its side effects.

People who are used to programming in dynamic languages primarily aren't used to having to think in terms of types and signatures, so this additional overhead contributes to their perception of promises as being too hard, too mysterious, or too much to think about. Programmers used to thinking about types are already thinking about this, so this mental overhead is negligible, and the abstraction of promises makes code easier to reason about simply because it let's them reuse the abstractions, patterns, and mental models they're already used to.

My relativistic assertion is that neither approach is correct, just as neither The British colour nor the American color is correct. This assertion is not without controversy, but neither are Promises. I appreciate the clarity that having type signatures gives me in communicating about code to fellow programmers (including my future self).

Finally, I want to clear the air. There is an unfortunate amount of FUD on the side of some promise proponents, whom I've heard say things like "callbacks aren't composable". It is possible to do metaprogramming on callbacks, including things like control flow manipulation (parallelization), map/reduce, decorator pipelining, etc. It's just that this needs to be done either in an ad hoc way or using some purpose-built library like async (or any one of the dozens of other control flow libraries in npm). These are not always clear to reason about. I won't make any claims as to which is easier, but with promises, since they're just values, one can use the same value programming techniques like map and reduce that is typical in synchronous code.

1 comment:

  1. >>Programming in promises requires thinking about type signatures.<<


    Please understand that "People who are used to programming in dynamic languages..." might also be used to programming with Promises in dynamic languages :-)