On 6 July 2015 at 10:27, Chris Angelico rosuav@gmail.com wrote:

On Mon, Jul 6, 2015 at 7:50 AM, Sven R. Kunze srkunze@mail.de wrote:

Seems like we stick to this example once again. So, let me get this straight:

- I can add, subtract, multiply and divide real numbers.
- I can add, subtract, multiply and divide complex numbers.
- I can even add, subtract, multiply and divide complex numbers AND real
numbers altogether in a single expression. 4) Granted, complex numbers can do more but that basically follows from their definition and does not jeopardize ease of usage.

Until you run into a TypeError: unorderable types: complex() > complex(), at which point you realize that they aren't a simple superset of reals with all the same operations supported.

Exactly. While complex numbers are a superset of the real numbers from a value perspective, from a behavioural perspective, there are things you can do when working only with real numbers that you can't do when you have to account for the fact that here might be complex numbers are in the mix. In a Python context, the essential operations specific to real numbers are listed at https://docs.python.org/3/library/numbers.html#numbers.Real

There's also a difference between the scope of the math and cmath modules:

>>> import math, cmath >>> set(dir(math)) - set(dir(cmath)) {'floor', 'pow', 'erf', 'trunc', 'copysign', 'expm1', 'ldexp', 'fsum', 'erfc', 'lgamma', 'frexp', 'gamma', 'factorial', 'log2', 'fabs', 'log1p', 'atan2', 'hypot', 'modf', 'radians', 'degrees', 'fmod', 'ceil'}

It's a general principle of design that expanding the kinds of values you accept (e.g. from real numbers to complex numbers) means you reduce the kinds of operations you can assume will work (e.g. to the behaviours of 2D complex numbers, rather than the 1D real number line). Similarly, as you step further down the numeric tower from real numbers to rationals and integers, you reduce the set of supported values, but you also increase the number of defined behaviours.

When it comes to coroutines and subroutines, coroutines are the superset - a subroutine is just a coroutine that never suspends before producing a result. You can thus make certain simplifying assumptions when executing subroutines that you can't make when executing a coroutine.

It also turns out that "never suspends" is actually problematic, which is why nominally subroutine based code these days tends to instead have *implicit* suspension points based on either operating system level pre-emptive multithreading where suspension may occur at any point or greenlet style state switching where suspension may occur as part of any function call (whether an explicit call or as part of a syntactic protocol). These approaches provide parallel execution at the expense of the ability to reason locally about code correctness, which then causes all sorts of *other* problems.

That said, I think there's definitely value in providing a very simple answer to the "how do I make a blocking call from a coroutine?" question, so I filed an RFE to add asyncio.blocking_call: http://bugs.python.org/issue24571

I'm less convinced of the value of "asyncio.wait_for_result()", so I haven't filed an RFE for that one.

Regards, Nick.