6 ms·
The performance criticism is a common one, but I don't think most people actually know what they're talking about. I certainly don't, since I don't use exceptio
by jstimpfle 8y ago
The performance criticism is a common one, but I don't think most people actually know what they're talking about. I certainly don't, since I don't use exceptions, but I've heard many respected engineers say that they needn't be slow (it depends greatly on implementation).
It shouldn't matter much anyway: You usually don't optimize for exceptional cases, especially if they lead to program termination.
And furthermore, you should not make heavy use of exceptions anyway :-). They lead to non-local control flow that is extremely hard to follow. They are nice for short scripts where they print a stack trace and abort the program. But they are a huge maintenance burden in larger programs.
- steego 8y agoYou are correct that the performance of exceptions isn't typically needed because exceptions aren't meant to be thrown that often. They're exceptional. Railway oriented programming is better suited when errors are much more common in the ringtone and when their handling is a fundamental part of the rules you're encoding. One friend uses a Railway oriented approach precisely because throwing that many exceptions per second (I don't know the exact number) would severely undermine the performance of their system.
- jstimpfle 8y agoWhat is an example of exception that happens many times per second? And where it could not be easily avoided to step on the error path at all?
- steego 8y agoSo I'm not talking about exceptions, I'm talking about talking about errors. You can easily generate thousands of errors per second if you're simply validating and routing large streams of data.
- jstimpfle 8y agoIt's nice to get concrete and leave the abstract shit talk (of which I've been guilty many times). So let's focus on that problem you mention and not argue errors vs exceptions terminology. In the case you mention I would clearly make two streams of data. In the simplest case, two append-only dynamically allocated arrays, one for parsing errors and one for parsed results. This is very likely possible to do - unless one needs an exact ordering, in which case it could be still possible to add positions to each of the streams. It's intuitively clear to me that this approach will be significantly faster than a Result<>/Either based approach. Because control flow is much simpler. There are two consumers, one for the error stream, and one for the parsed stream. There is no switching between the cases on the consuming side, and complexity is much reduced because each part can do one thing and do it well. You also get the freedom to process the streams independently, e.g. at different points in time, or not at all, etc. It's much simpler - if you try this route once, you will ask yourself afterwards, how have you ever endured this complex algebraic data types stuff. Yes, you could still add a consumer of those Result<> values that demultiplexes them in two. But nobody does that. Because, why did we introduce Result values in the first place then? This is just one of the many examples I encounter daily and that demonstrate how advanced type systems and ideas like OOP or FP encourage bad program structure. They are just not good ideas.
- mdpopescu 8y ago> Yes, you could still add a consumer of those Result<> values that demultiplexes them in two. But nobody does that. What? That's exactly how you consume those values. In pseudocode: match result when success: ... when error: ...
- jstimpfle 8y agoThis is not demultiplexing into two different data streams, but handling both clumped together. Exactly what I was speaking of: Avoiding this clumping and processing the cases as separate streams (and independently!) brings huge payoffs. Just try it; you will realize how much simpler your application gets - so many problems simply go away if you make more homogeneous data streams, and not fewer heterogeneous ones. And so many possibilities magically open up. Very briefly, here is the difference: def process_both_cases(stream): for item in stream: if case1: do_complex_stuff_1(item) elif case2: do_complex_stuff_2(item) else: assert(False) vs def process_case1(stream): for item in stream: do_complex_stuff_1(item) def process_case2(stream): for item in stream: do_complex_stuff_2(item) I think it's immediately clear how much better the second version is. It's less complicated: The conditionals are not needed. Processing the cases is decoupled.