Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
222 views
in Technique[技术] by (71.8m points)

javascript - What are the implications of the recursive joining of Promises in terms of Monads?

I know that Javascript's promises are technically neither functors nor monads in the sense of Haskell, because (among other things)

  • they include a bind operation that falls back to map when a pure function is passed in (and thus has an ambiguous type)
  • both the Promise constructor and resolve (aka return) join nested promises recursively

The first issue can easily be bypassed by always providing a function with the right type a -> Promise b.

The second issue obviously violates the parametricity trait of parametric polymorphic functions, i.e. one cannot construct a m (m a) structure. But what would this structure mean in the context of promises/asynchronous computations? I cannot think of a meaningful semantics for Promise (Promise a), where Promise is a monad. So what do we lose? What are the implications of the recursive joining?

Provided we are pretty pragmatic (and that's what we should be when we're programming Javascript), can't we claim that a Promise is a monad in Javascript if we take care of the edge cases?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The first issue can easily be bypassed by always providing a function with the right type a -> Promise a.

Or by not using then as the bind operation of the monad, but some type-correct ones. Creed is a functionally minded promise library that provides map and chain methods which implements the Fantasy-land spec for algebraic types.

The second issue can be bypassed as well with the same approach, by not using resolve but fulfill instead, and the static of method as the unit function.

But what would this structure mean in the context of promises/asynchronous computations?

It's a promise for a promise for a value. Not every constructible type needs to be "meaningful" or "useful" :-)

However, a good example of a similar type is provided by the Fetch API: it returns a promise that resolves to a Response object, which again "contains" a promise that resolves to the body of the response.

So a Promise (Promise a) might have only one success result value, which could as well be accessed through a Promise a, however the two levels of promises

  • might fulfill at different times, adding a "middle step"
  • might reject with different causes - e.g. the outer one representing a network problem while the inner one represents a parsing problem

Notice that the Promise type should have a second type variable for the rejection reason, similar to an Either. A two-level Promise err1 (Promise err2 a) is quite different from a Promise err a.

I know that Javascript's promises are technically neither functors nor monads in the sense of Haskell

You haven't mentioned the biggest issue yet, however: they're mutable. The transition from pending to settled state is a side effect that destroys referential transparency if we consider execution order, and of course our usual use cases for promises involve lots of IO that isn't modelled by the promise type at all.

Promise.delay(50).then(() => Promise.delay(50))
// does something different than
const a = Promise.delay(50); a.then(() => a)

Applying the monad laws is fun and occasionally useful, but we need lots of pragmatism indeed.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...