Hacker News new | ask | show | jobs
by stickfigure 1086 days ago
This is also nonsense. 99% of the time these failure modes are irrelevant. A remote call fails, the error propagates up the call stack, and someone gets an error message. Just like any of the thousands of other things that can produce errors in complex systems.

In the rare case you need to harden a particular call, you add caching or retries or whatever other logic fits your use case. It matters not one bit whether you're using futures or synchronous rpc stubs. Actually it does - synchronous code is easier to harden because it's easier to reason about.

Even javascript added await because it's better to pretend that async code looks synchronous. The failure semantics of "throws an exception" are just fine.

1 comments

> A remote call fails, the error propagates up the call stack, and someone gets an error message.

Uh-huh, but did the message actually get through? Can they safely just retry? These are very uncommon failure modes on local systems but very common on networked systems. Without a proper stateful abstraction beyond just "procedure call", like a promise, you can't address these failure modes properly.

> In the rare case you need to harden a particular call, you add caching or retries or whatever other logic fits your use case

Which now makes your system nondeterministic like I said.

> Even javascript added await because it's better to pretend that async code looks synchronous

Yes, linear code is easier to read. I don't see what this has to do with anything. The use of promises and await indicates a possibility of failure semantics that would otherwise not be apparent in the program's control-flow.

Yes, you can superficially make this look like synchronous code, but it's not synchronous code.

> Uh-huh, but did the message actually get through? Can they safely just retry?

...

> The use of promises and await indicates a possibility of failure semantics that would otherwise not be apparent in the program's control-flow.

They don't, though. They don't indicate if the message got through. They don't indicate if you can safely retry. Their failure mode is exactly as opaque or as transparent as synchronous calls.

The reason for their existence and mandatory use in Javascript is due to a deficiency of the platform (single thread, so all synchronous calls block).

If the platform was better they would never have existed.

> They don't, though. They don't indicate if the message got through. They don't indicate if you can safely retry. Their failure mode is exactly as opaque or as transparent as synchronous calls.

The promise is at the remote end. Promise resolution is idempotent, so retries always resolve to the same value. These are correct promise semantics as pioneered in the E language.

> The promise is at the remote end.

What? The promise is local, so what do you mean "the promise is at the remote end?"

> Promise resolution is idempotent,

No. It is only run once (whether resolve or success), that does not mean it's idempotent.

Just like exceptions for synchronous code, or error returns.

Nothing personal, but people not aware of where these concepts came from in distributed computing history is annoying, and I don't like repeating myself:

http://erights.org/elib/distrib/captp/index.html

Regardless of where promises come from, the fact is that they are locally resolved, not remotely resolved.

Using promises in JavaScript is a hack around the fact that the platform has some pretty large deficiencies.

> you can't address these failure modes properly.

Again, total nonsense. There's nothing special about a promise. Whatever logic you can build on promises is easier to build synchronously. Everything that applies to building distributed systems applies whether you use rpc stubs or promises. Promises are just noisier and harder to reason about.

Maybe we're speaking past each other. Idempotent operations are implicit promises. Network partitions require idempotency at some level to ensure robustness. That means any robust distributed protocol requires promises at the core protocol level.

Trying to hide the promises behind a synchronous client interface is unnecessarily constraining and inefficient, like requiring large stack contexts that can't be restarted or persisted, and so can't be simply resumed after partitions.