5 ms·
This post is poorly named. What it's saying is to avoid throwing exceptions for normal flow control, and just use them for exceptional cases (file not found, et
by kstenerud 4y ago
This post is poorly named. What it's saying is to avoid throwing exceptions for normal flow control, and just use them for exceptional cases (file not found, etc).
Performance-sensitive code or not, exceptions for exceptional situations are not going to hinder the app's performance until the exceptional situation becomes common (unless your language does lots of exception setup work on the happy path - which most have stopped doing by now).
- Gigachad 4y agoSeems like a language design flaw that this is slow. Imo Rust got this right by making "exceptions" nothing special, just a data type holding an error which can be processed just as fast as anything else.
- polynomial 4y agoWhich is not to suggest you should be using exceptions for flow control.
- bhawks 4y agoPython's StopIteration has entered the chat.
- Izkata 4y agoI remember reading a long time ago that cpython had done some work to make exceptions lightweight enough for it to be reasonable to use them for control flow... No idea what that was though.
- TeMPOraL 4y agoWell, if that's the only "escape hatch" you have, because otherwise the language authors decided to limit what you can do with control flow...
- nosianu 4y ago> Seems like a language design flaw that this is slow I think stack traces take up much of the time, including converting it [1]. Without them it could probably be a lot faster. Also see hashmash's post. But other than that, there is the logic issue, not using exceptions for normal flow control makes sense at least to me too independent of any performance questions. [1] For a Java example: https://ionutbalosin.com/2018/06/getting-the-stack-trace-versus-throwing-an-exception-what-is-common-and-what-is-different/ https://ionutbalosin.com/2018/06/getting-the-stack-trace-ver...
- aidenn0 4y agoThis is another reason why Common Lisp style condition system (where exception handlers can execute without unwinding the stack) just seems to work better. If you need the stack, get the stack, if you don't then just use handler-case. I really don't see the downside for any language that has anonymous function literals.
- lmm 4y agoJava has them now but it didn't at the time (and even now they're kind of bodged in IIRC).
- tsimionescu 4y agoI don't think Java has support for CL condition system-style restarts. You're maybe talking about exceptions that simply don't populate the stack trace? To clarify a bit, in CL, when throwing an exception, you can optionally register one or more "restarts", which are essentially lambdas of 0 or more parameters. When the exception is thrown, the stack is walked to find the appropriate handler, but it is not unwound. Whoever catches the exception will also receive these restart lambdas. If they chose to call one of the lambdas (passing it the proper parameters), stack unwinding will not happen at all, and execution will continue from the place the exception was thrown. Only if none of the restarts are invoked is the stack unwound, and execution continued from the catch block. For a somewhat trivial example, the CL runtime throws an exception whenever a variable that was not defined is being read. That exception includes a restart that allows you to define a value for the variable - if this is called, execution will continue where the variable was being read, using this value for the undefined variable. Of course, this would be crazy to do automatically, but it is very nifty when debugging, as this option is presented to you in the REPL.
- celeritascelery 4y agoExceptions are not the Error/Result type in Rust, they are panics. Which can be very expensive. But thankfully rust does them right and only uses them for unrecoverable errors.
- Gigachad 4y agoYou're right, what Rust gets right is standardized errors that aren't exceptions. In other languages you pretty much just return {success: false, message: ""} or whatever million combinations of this idea people sprinkled in the code. Having a single type handle this with a bunch of utility functions associated is great.
- elcritch 4y agoNot sure I'd agree. In my limited experience its a hassle with tons of different error types that need converting. You end up converting LibAError to LibBError, etc. Thats tedious so many people use `thiserror` or `anyhow`, where `anyhow` just hides the error types behind a `&dyn Anyhow::error` or whatnot. Since stable Rust lacks compile time or runtime introspection its difficult to figure out whats wrapped up in the dyn Trait without knowing ahead of time. Inside a single library its not too bad. You get probably an enum type with some errors. But adding stacktraves or anything is a pain if possible at all.
- saurik 4y agoWhen exceptions are used for actual exceptional cases, that is both slower in the happy path (as you are having to add a ton of comparisons for the error case, vs. zero-cost exceptions as are now commonly deployed) and more verbose (though Rust has been working to mitigate this over the years, they still failed to realize the people who made the kind of error object popular also designed an abstraction for monads and a syntax sugar that made using the result/either object "look like" exceptions) than exceptions.
- elcritch 4y agoDoes OCaml use that exception like setup?
- saurik 4y agoYes and no: OCaml supports exceptions, uses them extensively in the standard library, and most guides encourage people to use them for exceptional cases (i.e. where an error is not an expected use case and the caller probably will just propagate the error rather than try to "handle" it... which I would assert is true for almost all error conditions: you should essentially never "handle" errors or "catch" exceptions), so it doesn't really need such syntax... and yet (apparently? I am not an OCaml developer, having only used it enough to demonstrate it for a class I taught a while back) it was finally introduced in a recent version ;P. https://jobjo.github.io/2019/04/24/ocaml-has-some-new-shiny-syntax.html https://jobjo.github.io/2019/04/24/ocaml-has-some-new-shiny-...
- tzhenghao 4y agoThis! I'd like to think exceptions are reasonable as a terminal state for a particular user workflow, which sometimes even end up having the program terminated. Examples include "unexpected I/O, corrupt files etc." Exceptions should never be used if the control flow has means to recovery. For example, if-else statements with more different, downstream instructions.
- 9dev 4y agoWhat about, say, a webhook server that must call User-provided webhooks, and retry them on failures, or handle maximum request duration? I’d argue throwing well-nested exceptions, catching them and retrying is the most elegant solution to this problem there is.
- tremon 4y agoIt also depends on the language. For example, Python very much operates under the "ask for forgiveness, not permission" mantra, and I even see this used for dict lookups. The number of times I encounter code like this: try: my_dict[key] catch KeyException: pass rather than if key in my_dict: my_dict[key] is astounding. I don't know what the performance difference is in Python though.
- 390iuwjhf 4y agoWhat about thing = my_dict.get(key) if thing is None: ...
- tremon 4y agoyes, my_dict.get() is my preferred solution too, if I can come up with a sensible default value or action. Otherwise, I do my_dict[key] without try/except. But I'm not sure if that's just a style preference or if there's a large performance difference in Python.
- rightbyte 4y agoThe later should require two lookups unless it is optimized away. Is there an easy way to get bytecode for Python snippets?