On Oct 31, 2019, at 16:24, Steven D'Aprano <st...@pearwood.info> wrote:
> 
>> On Thu, Oct 31, 2019 at 02:47:35PM +0100, Andrew Barnert wrote:
>>> On Oct 31, 2019, at 13:56, Steven D'Aprano <st...@pearwood.info> wrote:
>>> 
>>> I disagree. I think it's a pretty small breaking change,
>> 
>> From my test earlier on the thread, using a function to raise makes a 
>> function that does nothing but raise and handle an exception take more 
>> than twice as long. Which implies that the actual raise part (not 
>> counting the outer function call, handling the exception, and 
>> returning) is far more than twice as long.
> 
> I don't recall seeing your test code, but I don't follow your reasoning 
> here.

You replied to a message from Ben that was a reply to my message with the 
performance test in it, which Ben was referring to in the chunk you quoted.

> Unless you are using a custom Python build where the raise 
> statement has been replaced by a raise function, I don't see how you can 
> conclude that the raise statement inside a function takes "far more than 
> twice as long" as a bare raise statement.

    def throw(e): raise e

Test functions using throw(ValueError("something")) vs. raise 
ValueError("something").

> So on my machine, raise-in-a-function is about 70% slower than raise, 
> not 100% or more.

OK, and it’s possible that on a different machine those functions would be only 
50% slower instead of 70% or more than 100%. So what? You can’t answer “it’s 
too slow” with “it’s only too slow on some machines”, much less “it’s only too 
slow on some machines; on others it’s arguably maybe on the border of 
acceptable”. It’s still significantly slower.

> That's a pure-Python function. It's possible that a 
> builtin function in C will have less overhead.

Sure, while I already mentioned in the previous message. Other things you 
didn’t address include the cost of looking up a builtin (which I don’t think is 
a big deal, but the OP was worried about it, and your test doesn’t include it)… 
but better if you go back and read the thread you’re replying to rather than me 
trying to summarize it badly.

> But to paraphrase the prevailing opinion expressed in the "word list" 
> thread, you cannot possibly be seriously worried about a few micro- 
> seconds difference. (Only slightly serious.)

But this is different. Parsing a global constant is something you do once, at 
module compile time, so who cares about a few us. Printing is already slow, and 
you already can and do work around that when performance matters, so who cares 
about a few us. Raising exceptions is something you’re forced to do all over 
the place. Maybe thousands of times per second. So you may need to care about a 
few us.

For example, if I need to Iterate through a ton of tiny sequences, each one of 
them has to raise StopIteration. A ton of times in the middle of my code is not 
the same as one time at module compilation. 

> In this case, I'm not that worried about micro-benchmarks unless they 
> can be shown to add up to a significant performance degradation in 
> macro-benchmarks too.

I think the onus is on people who want this feature to prove that it doesn’t 
affect the performance of real-line code, not on people who don’t think it’s 
necessary to prove that it does.

>> Also, there are many places in the Python data model where you have to 
>> raise an exception, such as StopIteration;
> 
> Does the Python data model not have function calls? *wink*

Yes, it does. So if you proposed something that made every function call 100% 
slower on my machine (even if it was only 70% slower on yours, even if it was 
only a matter of a few microseconds), then I would be raising the exact same 
objection.

And if it also broke backward compatibility, then I’d be raising that objection 
as well.

>> And what’s the advantage?
>> 
>> If we need raise inside an expression without extra overhead, this 
>> doesn’t solve the problem; we’d need a raise expression (similar to 
>> the yield expression), not a raise function. 
> 
> Yield expressions are a red herring here.

No, they’re a model for how you can turn a statement into an expression between 
Python versions without breaking backward compatibility. 

That doesn’t quite prove that the same thing can be done here, but it does 
alleviate the concern (raised earlier in the thread) that someone thought it 
was unlikely that you could possibly do such a thing.

> Yield expressions are a weird 
> syntactic form that allow you to feed data into a running generator. 
> raise doesn't need to do that. The raise function will never return, so 
> it doesn't need to return a value. It will be kind of like os._exit, and 
> sys.exit.

A function that never returns is no less weird than an expression that never 
has a value. (Especially given that the only way to use a function is with a 
call expression anyway.) They’re both equally weird. But exceptions are one of 
the handful of cases where you expect it to be weird—along with things like 
exit and abort and break and return. The only way to avoid that weirdness is to 
do what Python (and C++ and some other languages) do with about half of them 
(including raise), and make it a statement.

I think the status quo is fine, but if there really is a need for raising in 
the middle of a lambda or a comprehension or whatever, it has to be an 
expression, and I think it makes more sense for that expression to still be 
custom syntax rather than a magic function.

>> If we don’t care about the overhead, there is no problem to solve; you 
>> can already get this behavior with a one-liner. If that’s still too 
>> inconvenient or undiscoverable, we can add the one-liner to builtins 
>> without removing the statement at the same time. So, what’s the 
>> benefit of removing the statement?
> 
> "Only One Way To Do It" *wink*

> We could have added a print_ function and kept the print statement too. 

Yes, but that case was different, for the reasons already discussed (print 
doesn’t affect flow control and doesn’t need any magic to implement as a 
function—you can even write it in pure Python; performance wasn’t an issue; 
there’s sometimes a useful benefit to shadowing a print builtin; breaking 
backward compatibility was less serious of an issue in 3.0 than in 3.9; …).

> If, back in the early days of Python, Guido had made raise a function, 
> we'd all accept it as normal and desirable. I doubt anyone would say "I 
> really wish it was impossible to call raise from an expression without 
> having to write an unnecessary boilerplate wrapper function".

If he’d made raise a syntactic expression rather than a statement, would anyone 
be saying “I really wish it was slower to raise an expression—and, even more, I 
wish it used a magic function that I can’t write myself, and that it was less 
recognizable as flow control (especially since half the IDEs and colorizers 
color it like a normal function)?”

If raise had been a function, we probably wouldn’t complain (except maybe for 
some people asking why it’s gratuitously different from all the familiar 
languages). But maybe we’d have been much more apologetic or much more evasive 
in all those “why Python isn’t too slow” blog posts when it came to the 
exceptions section. Plus, every time some new mechanism was designed to use 
exceptions, like StopIteration, there would have been a more serious objection 
to overcome, so we might have ended up with a different and clunkier design.

> As I said above, I'll grant you the *possibility* that performance may 
> tilt the balance, but honestly, I see no other reason why we should 
> prefer a *less flexible, less useful* raise statement over a more 
> flexible, more useful function.

More flexible isn’t always more useful. I’m not convinced that people really do 
frequently need to raise in the middle of an expression.

And performance is far from the only consideration—there’s backward 
compatibility, and looking like flow control, and so on. None of which are an 
issue for a raise expression—which solves the same problem, if it really is a 
problem that needs to be solved.
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/UZG6KON7453ZSOA5S2KGVOCPMNRKSO7P/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to