Why would you write this: [long_io_operation with callback]
When you can instead write this: [long_io_operation without callback]
Off the top of my head, I'd say because "var result = long_io_operation(req);" is not only not self-documenting but actively deceptive if long_io_operation is an asynchronous call.
I'm not sure I see what's to be gained by pretending closures don't exist.
It's not deceptive if you have a preemptively multitasking runtime that you trust (like the Erlang VM); it's a much more natural way of writing code. Single-threaded, event-driven callback spaghetti makes code harder to read, and inherently means more code.
If someone took v8, added lightweight coroutines, and wrote a preemptive scheduler for it, I bet you could massively reduce the SLOC count for large node.js apps.
But we aren't talking about Javascript. It's about a theoretical replacement language.
Also, the reason you care about the "synchronicity" in the first place is because your environment forces you to care! When it all Just Works (TM), you don't care whether it's "synchronous" or not. That's a deficiency in your language, not a feature.
I'm not theorizing. I program in this sort of language all the time. You worry much more simply about how long something will take, which you can never not worry about, rather than how many bits are "synchronous".
It is not that simple in real life, unfortunately. Even ignoring the fact that inlineCallbacks's overhead is significant, inlineCallback forces you to serialize things which may be run in parallel. If you do
yield f1()
yield f2()
You force f1 to finish before being able to start f2, which goes against the whole point of using something like twisted in the first place ! I have also noticed that inlineCallback screw up the traceback (e.g. http://twistedmatrix.com/trac/ticket/3622 - I still see this with twisted 10.0.0).
While I am no genius, I am a decent programmer, with quite a few years of experience in python, and I still can't read most non trivial async code in twisted with confidence. It is almost impossible IMO to get a clear idea of the codeflows and especially error flows to ensure error handling is done right. Error handling is already difficult as is, adding exception made it that much harder, but adding async makes it basically impossible except for Vulcans.
In real life inlineCallbacks's overhead is not significant compared to typical IO times (even snail in a park reaches its destination sooner than a rocket makes it to Pluto and back).
inlineCallbacks allows you to write code in a synchronous manner (that's the point). If you'd like to wait for completion of both f1 and f2 functions simultaneously you could:
yield DeferredList([f1(), f2()])
The ticket you mentioned is closed as invalid. Use `failure.printTraceback()`.
Non trivial async code is hard to read (period).
Exceptions allows you deal with errors in the place where you know what to do with them, not in the place where they arise (as in any other Python code).
inlineCallback overhead can certainly become significant. Saying that it is not an issue because it is nothing compared to IO only works if you are not CPU bound in the first place - but for many applications in python, you are CPU bound.
Using deferred lists is a fair point, but then you are kind of back to using deferred directly.
As for tracebacks, I used the ticket as an example: if you have to use failure.printTraceback, it means you caught the exception, which means you need to catch them everywhere. But the problem is when there is a big in your application because you forget to handle an exception - debugging those is a PITA because you don't get the right traceback.
So yeah, async code is hard, inlineCallback sometimes helps, but all this really feel like a big kludge to me - I would rather use a framework where async is abstracted away. Doing all this by hand does not work very well for complex applications.
Off the top of my head, I'd say because "var result = long_io_operation(req);" is not only not self-documenting but actively deceptive if long_io_operation is an asynchronous call.
I'm not sure I see what's to be gained by pretending closures don't exist.