IO::Handle::close shouldn't decide what's a failure #6055
While trying to work around #125757 (using :out
The docs for IO::Handle::close say only:
Will close a previously opened filehandle.
But other close methods mention using a LEAVE
like $message, rx:i/invalid/, 'Invalid option warns';
Now, indeed this returned a non-zero exit status.
Beyond that, there are many programs that use a
I wouldn't mind the ability to throw an exception
Would you be able to give a link where LEAVE is recommended?
$proc.err.close doesn't throw; it returns the Proc object. The throwage happens when a Proc with non-zero exit status is **sunk**.
At the very least, this is a documentation issue.
So after reading this wall of text I'm still confused. What is the actual problem that should be solved here?
For example, let's say you have something like this (dumb example, but still):
Last part of the ticket implies that we shouldn't blow up just because it has a non-zero exit code (“Perl 6 doesn't know these, and I don't think it should make decisions at this level”). In which case it can be rephrased to “here you have a safety feature that prevents silent failures, please remove it because I know better”. OK, but why are you running grep from perl 6 ?… If you really have to do that, then you can probably write an extra line of code to explicitly say that you expect non-zero exit codes.
The title, however, says that .close shouldn't throw. But the same logic applies here as well. If we make it not throw, then your code may have silent failures and you won't even notice. So handle it explicitly.
In other words, I think this should be rejected.
On 2017-02-03 18:27:34, comdog wrote:
I found where the LEAVE was mentioned and removed it.
The .close you're calling is actually from IO::Pipe, not IO::Handle.
Probably something else should be clarified in the docs to avoid that sort of confusion as well.
Marked it as TODO for my IO grant thing and will resolve this ticket as part of it.
You do not have to catch the exception. Just don't sink your Proc. For example:
Of course, in actual code you'd have a proper check and no workaround would be needed:
say ‘oops!’ unless shell(‘exit 1’)' # or whatever the normal exit code in this situation is
But given that your use case is “testing”, then a .so won't hurt.
On 2017-02-13 12:46:20, comdog wrote:
Please note that not checking the exit code is an error in almost all cases. For example, what if something you are running segfaults? Are you going to say that this is what you meant it to do?
On 2017-02-13 13:05:59, email@example.com wrote:
If something segfaults, that's a different issue (that I haven't
But, notice in the example I provided in this report, I am checking
Well, in your particular example, I'd probably use something like:
my $exit-code = $proc.err.close.exit-code;
And that'd be it.
Maybe you wanted something like this:
Which, interestingly, hides the feature. But note that *this is not specced*.
On 2017-02-13 13:24:00, comdog wrote:
This comment is technically redundant. And maybe not helpful. I apologize if it's annoying to anyone.
TL;DR; brian's right that built-ins shouldn't decide what's a failure in all but extremely exceptional circumstances (eg segfaults). Fortunately, they don't.
That's crazy. I was going to ask where that was in the docs but reading through this thread it looks like Zoffix found it and deleted it.
This is a doc problem, not Perl 6.
(Well, except bugs. If anything below doesn't work, there's a good chance it's a bug.)
Most Perl 6 routines are Socratic about apparent failures: they know that they do NOT know what the caller knows.
Likewise Perl 6 itself; it generally gives callers of routines 100% control over how to interpret the results of any call.
Here's an attempt to explain this.
1. Every routine body must return a value. There are no exceptions. (Pun intended -- and meaningful: in almost all cases for built-ins, at the point immediately after a value has been returned, no exception will have been raised even if a possibly catastrophic failure has occurred, precisely because it's up to the caller to make the final decision.)
2. A routine may return a Failure. This means the called routine wants the caller to decide what to do about something that *might* be a problem that needs attention.
3. Calling code must explicitly say when a possible problem, as signaled by a returned Failure, is not *actually* a problem (or at least that the calling code's got it covered). This avoids the problem of *accidentally* forgetting to handle real problems.
4. For example, if `some.expression` is in sink context (it's just a statement on its own) and its runtime value might be a Failure, and you know that this wouldn't be a problem, at least for now, then here are your main choices:
4.1 `sink some.expression;`
In stark contrast to implicit sink context, explicitly sinking `some.expression` causes Perl 6 to silently throw away whatever value is returned, even a Failure.
4.2 `try some.expression;`
This stores any Failure in `$!`
4.3 `some.expression.so;`, `some.expression or True;` etc.
Testing a Failure with a boolean test or a defined test "disarms" it. (Though `so some.expression;` and `?some.expression;` don't work for some reason.)
5. Finally, if you do NOT *explicitly* `sink` a Failure that's in sink context to throw it away, and you do NOT `try` the expression that returned it to store problems in `$!`, and you do NOT disarm the Failure by testing it; if you do none of these things and then you use the value as an ordinary value, Perl 6 throws the til then unthrown Exception that sits inside the Failure.
Aiui, this only became an exception because your calling code didn't signal "I've got this" by explicitly `sink`ing or `try`ing or testing a routine's return value.
To recap, Perl 6 is not (supposed to be) making a final decision.
Even throwing an exception isn't a final decision -- calling code can handle exceptions.
But, much more importantly, Perl 6 waits until all other avenues are exhausted first.
Instead, Perl 6 makes it easy for called routines to return a value indicating something might be amiss and easy for calling code to handle that situation.
I think that's `use fatal;`. See https://docs.perl6.org/language/control#fail
Yes. They just have to be explicit, eg `sink some.expression;` or `try some.expression` or `some.expression or ...` or `some.expression.so`, if they're calling code that sometimes returns a value indicating a possible error, eg a Failure.
On Mon, 13 Feb 2017 21:04:37 -0800, raiph wrote:
Not annoying, but I feel we all have thoroughly confused bdfoy by now :P
Some minor corrections:
That's inaccurate. `sink Failure.new` will explode the Failure, just
That will store the exception the Failure contained in $!,
Basically anything that ends up calling .Bool on Failure will mark it
<ZoffixW> m: say ?Failure.new
There are actually no failures involved in the original code. The throwage
This means `try $proc.err.close` won't stop the explosion, because
On Tue, 14 Feb 2017 11:25:44 -0800, firstname.lastname@example.org wrote:
Wow. I should probably not be writing another comment given how messed up my previous one was but for the record:
That makes much more sense.
I must have been more tired than I realized. Now of course they work for me too. /o\
Thanks for cleaning up after me.
I want to say more. But in the hope that Brian won't actually think this all just proves he's right that Failures and/or Perl 6 sucks, I won't. ;)
On Fri, 03 Feb 2017 18:27:34 -0800, comdog wrote:
Thank you for the report. However, I'm going to reject this ticket, as
- "IO::Handle::close shouldn't decide what's a failure"
-- IO grant