Open treyharris opened 5 years ago
Personally, I like the X::UserMessage
solution best, though I’m not the greatest at naming things—the concept, not the name. If others like it, I’m happy to draw up a proposed class spec and changes to the docs with example code to show what it would look like in action.
On #perl6-dev this was discussed just now at some length, and a couple things to note:
There’s an &*EXIT
that can be reset as necessary—useful for cases where you aren’t running as a POSIX process in your own right. But it can’t affect the error output, which is the primary issue here.
exit
itself is a multi defined on Int:D
, so one way to skin this cat that could be done entirely through a CPAN module (which isn’t possible for something that would need to reach out into the top-level and install a CATCH
) would be to have variants of exit on Str and Exception. Unless someone else has strong feelings about a different option I may try to hack that up and see how it feels.
It would mean that instead of doing die
for user-oriented errors, you do
exit "This is not a graphical display, I can't draw anything!";
# or, equivalently,
exit X::UserError::NoGraphics.new();
and it would note
before redispatching to exit 255
. Perhaps a new global is needed so that you can influence where fatal messages go rather than using note
explicitly?
But even as now stands, assuming these exit variants existed, then, for example, a single program that can run in a CLI or a GUI could deal with this by redirecting $*ERR
(perhaps via a filehandle-like buffer) and changing &*EXIT
so instead of writing to standard error and exiting, you got a fatal dialog box with the message.
I don't like exit
since it breaks my LEAVE
phasers. (I'd love for that to change, btw. Perhaps that should be a separate issue -- What is a good way to finish any open LEAVE
phasers, then exit the process with a non-zero code?)
I've been making a CATCH
in a proto MAIN
(so it works for all my multi MAIN
s) that prints the way I want for users.
@CurtTilmes It’s hard to get the CATCH
clause right, though—a small one (such as is documented in the die docs) doesn’t work right in some circumstances, and a general one is quite long and hairy. (It feels to me analogous to how hard it was in Perl 5 to write while (<>) { ... }
from scratch if you wanted to tweak it slightly.)
With the X::User
class hierarchy with different top-level handling, I’m advocating the implicit behavior to obviate an explicit CATCH
in almost all cases.
I hear you with regards to exit
, but I don’t see an alternative that can be done via a CPAN module, do you?
In Perl 5,
die
’s message could be terminated with a newline to suppress diagnostic messages (line number at minimum, up to full backtrace if you ask for it—in this issue I’ll just call it “backtrace” for simplicity, whatever it is—it’s anything added by the language to the message given todie
).This suppression via newline is no longer possible in Perl 6 (and for very good reasons that I shouldn’t have to mention here).
It does leave an open question, though: in directly interactive executables, how should the programmer idiomatically handle end-user errors such as incorrect usage (apart from
USAGE
itself, of course)—things like attempting an operation the user doesn’t have access to, trying to run a graphical program in a non-graphical context, etc.? It seems like the choices are:Use
die
anyway and put up with the backtrace printed to users.CATCH
-all at the top level for production code that turns the language backtrace into something that can be communicated by nontechnical users to support, since user errors are not something that needs such special handlingFollow the method recommended in
die
’s docs: create a top-levelCATCH
-all which changes the top-level uncaught behavior so it doesn’t print the backtrace, and then usedie
.die
’s documentation and will contrive a solution withexit
instead. b. For executables I’ll call “targeted at the semi-technical user”, it’s common practice to exit with nice-human messages for things that the program checks, but to “toss its cookies” with a backtrace if it hits an exception unaccounted for. Besides being most parsimonious for the programmer, this seems like very reasonable behavior for users who are likely to know the difference between most “my fault” problems and “bugs”—and a top-levelCATCH
-all makes this more difficult to be the default behavior.Create an exception class (
X::UserMessage
?) that (along with its subclasses) restores the Perl 5die
-with-newline behavior; if it reaches the top level, only the message of the exception is printed, and not a backtrace.X::UserMessage
exceptions into one complete with instructions for reporting bugs with a simpleCATCH
at the top level that catches everything butX::UserMessage
. e. Following from the above, it makes the “semi-technical user” oriented behavior of nice messages for things you want to gently inform the user about, but “cookie tossing” for unexpected failures, the normal case without any special handling.CATCH
-all at the top level with no special-casing—but as mentioned in the last Pro point, it obviates theCATCH
entirely in the most likely use case where one would do that.Create a new word (maybe
exitwith
) to give some syntactic sugar todie
-with-newline behavior b. It’s simple c. It’s easy(Throwing this in for completeness:) Adopt the Perl 5 behavior of paying attention to a trailing newline. I mention it, but it seems (to me) patently ridiculous behavior for Perl 6. A not-so-crazy alternative, however, would be to add to
die
a backtrace-suppression boolean flag (with an accompanying boolean field in exceptions, I presume?) that would stop the backtrace if and only if the exception gets all the way to the top level uncaught.¹ I don’t think the promotion of proper exceptions is anything to sneeze at; I suspect that for programmers who use stringy ad-hocs for convenience’s sake, they probably have a very large overlap with programmers who only
die
at all for things they actually check for. So making this class of thing easier with real exceptions than with strings would be a win for non-ad-hoc exceptions.