SEH: the throw keyword - Forum - OpenEdge Development - Progress Community
 Forum

SEH: the throw keyword

This question is not answered

Matt Baker suggegested to start a new thread on the subject so here it is.

Interesting article on the throw keyword: hackernoon.com/the-throw-keyword-was-a-mistake-l9e532di

More on this f.e.:

www.monitis.com/.../
https://mattwarren.org/2016/12/20/Why-Exceptions-should-be-Exceptional/

Some new links I did not post in the previous message:

https://ruudvanasseldonk.com/2015/06/17/exceptional-results-error-handling-in-csharp-and-rust

a recent discussion on hacker news about the first article:

https://news.ycombinator.com/item?id=22158837

Btw another great article from the same writer as the first article ("Return to what works and jettison the fads"):

https://hackernoon.com/the-flow-manifesto-iq2732s1

@Gus : I think you will love the contents of the last link. ;-)

All Replies
  • I could only get to this point before I stopped reading:

    >> Our intrepid short-attention-span test-driven-developer is three levels deep in void functions and suddenly discovers a potential error condition. He doesn’t want to “refactor” three void functions to return error codes, he has a foosball game with the team in five minutes, so he issues a call to throw and wraps the call to the first of the void functions in a try-catch block and runs off to his game. Facepalm.

    The author clearly misunderstands SEH.  The main purpose of an exception is to carry an error-related output from a method back to its callers (client programs).  This output is used when *all* the other behavior of the method has been rendered irrelevant .   In other words the exception is already an *implied* output parameter (and more besides - see PS***).  So the author seems to be asking us to re-invent the wheel by putting errors in the returned results of all our methods, even though errors can already be sent back for "free".  Besides, the return value of a function is typically already in use for some other purpose that is much more *relevant* from a semantic standpoint. 

    I can just imagine trying to read the author's programs.  They probably look a lot like the WIN32 API did when it was built decades ago... every method returns some obscure 32-bit error number (https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- ) and nobody knows what the error number means until they look it up on some bureaucratic reference chart.  Also, every client of every method has to check the error result before proceeding to ensure that it returned a zero or whatever.

    Thank goodness for S.E.H. and for the fact that there are more people who understand its benefits than the ones that don't.  Note that appserver in OE 12 is being enhanced to marshall errors/exceptions all the way back to an openclient.  Thankfully Progress understands S.E.H. pretty well (with certain exceptions like the lock-wait timeouts that were designed to fall out of my last-ditch exception handling). 

    *** PS.  SEH is also a control-flow tool.  The language will give you catch/finally structures and will take you back down the callstack as far as needed.

  • This thread is marked as a question.

    What is the question here?

    Or are you looking to start a discussion?

  •  I will see that I write a longer response later, I do not have much time now. There are other languages that use other ways of error handling, for example haskell and rust. It might be worth to take a look into those before hastily deciding for what you know? If you do not like the contents of the first link try another:  ruudvanasseldonk.com/.../exceptional-results-error-handling-in-csharp-and-rust. The writer is holds a degree in computer science, he writes

      >>> In C#, exceptions are the standard way of doing error handling. I have written plenty of C# code that deals with exceptions, and it is a pain to do it correctly.

      >>> Apart from null, it might very well be the biggest design mistake of the language. [..]

  •  I will see that I write a longer response later, I do not have much time now. There are other languages that use other ways of error handling, for example haskell and rust. It might be worth to take a look into those before hastily deciding for what you know? If you do not like the contents of the first link try another:  ruudvanasseldonk.com/.../exceptional-results-error-handling-in-csharp-and-rust. The writer is holds a degree in computer science, he writes

      >>> In C#, exceptions are the standard way of doing error handling. I have written plenty of C# code that deals with exceptions, and it is a pain to do it correctly.

      >>> Apart from null, it might very well be the biggest design mistake of the language. [..]

  • Consider that exceptions aren't only a language concern.  They are also part of the .Net runtime environment.  If I write code in VB or C#, it is interoperable and the runtime will propagate exceptions across unrelated assemblies.  The JVM runtime also supports multiple languages and I'm assuming they all have to support the handling of exceptions or languages and libraries won't play well together.

    Without common patterns and practices, things get very crazy.  Imagine pulling ten nuget packages that each had their own unique strategies for reporting errors to the runtime!

    Sharing code in a runtime is possible because exceptions are a first-class member of the runtime.  And there is lots of developer tooling that is built on time of the .Net exceptions.  In the context of .Net, arguing against the concept of exceptions is almost as unthinkable to me as it would be to argue against another fundamental part of the runtime (like GC-based memory management or whatever).  

  • Just a fun fact, I've seen a document for a medical device safety inspection. It required that the programming language used handles SEH. They must love the catch-all error handling.

  • That is interesting.  I'd hate to write software for medical devices but, if I did, it would certainly involve SEH.  And I'd be extremely careful while crafting my catch blocks.

    In regular line-of-business code, I rarely catch all exceptions.  I try to catch the bulk of exceptions including the ones that are predictable.  But I only catch them as long as they aren't "above my pay grade".  If there is an out-of-memory, or full disk, or power failure, then those will (intentionally) escape my application catch blocks because I am not even going to pretend like I know what to do for them.  

    Normally it would be the job of the OS to log these types of unexpected crashes to the event log, and it can figure out what to do next ...  These types of things may even cause the entire VM to shut down and someone will need to allocate more resources to it from the Hyper-V host (memory or disk or whatever).  That type of thing is definitely above the pay grade of any given line-of-business application.

  • I have no strong opinion about SEH vs message/error code. I'm in the camp of consistency. Unless you are writing a webservice, use one or the other but not both.

  • I still do not have much time. I'm not using OE at the moment, just interested in errorhandling. I could write a lot about EH. MS warns:

    >>> Do Not Use Exceptions to Control Application Flow

    >>> Throwing exceptions is expensive. Do not use exceptions to control application flow. If you can reasonably expect a sequence of events to happen in the normal >>> course of running code, you probably should not throw any exceptions in that scenario.

    docs.microsoft.com/.../da0007-avoid-using-exceptions-for-control-flow 

    The man that wrote hackernoon.com/the-throw-keyword-was-a-mistake-l9e532di has very valid points.

  • You stated "The author clearly misunderstands SEH.  The main purpose of an exception is to carry an error-related output from a method back to its callers (client programs).  This output is used when *all* the other behavior of the method has been rendered irrelevant .   In other words the exception is already an *implied* output parameter (and more besides - see PS***).  So the author seems to be asking us to re-invent the wheel by putting errors in the returned results of all our methods, even though errors can already be sent back for "free".  Besides, the return value of a function is typically already in use for some other purpose that is much more *relevant* from a semantic standpoint."

     In another message you talk about .net.

     The author (of https://hackernoon.com/the-throw-keyword-was-a-mistake-l9e532di) that "clearly misunderstands SEH" discusses the same problems as is done in .net docs. In docs.microsoft.com/.../da0007-avoid-using-exceptions-for-control-flow two problems with throwing exceptions are named:

    - You obscure program flow and thereby make it less readable https://enterprisecraftsmanship.com/posts/exceptions-for-flow-control/

    - It is expensive performancewise

    That makes your statement 'errors can already be sent back for "free"' quite suspicious.

    I'm interested in your opinion, you already wrote something?

  • Control-flow is an integral part of the way exceptions work.  I agree that you don't throw exceptions for the express purpose of control-flow.  It's not supposed to be used as a GOTO statement.

    And I agree there is an overhead when using exceptions.  For example, it costs more than assigning an output parameter or a return result.  But since .net is a jitted platform, I think it would take a LARGE number of exceptions before a line-of-business application would start suffering.  

    Normally it is the out-of-process resources, like remote database, that incur the biggest performance penalties in line-of-business apps.  The "client-server" protocol in ABL, for example, will bog things down in a big way - which is proportional to number of packets exchanged over the intranet.  This vastly exceeds the CPU concerns related to using SEH in a jitted language.

  • I had a rant...and then deleted it after I read the article, so I'll start again.  I've been working with java as a simpleton since 1997.  C# a bit less. Thanks for the links to how RUST handles this, is good to read how someone uses a few language constructs to enforce handling of no nulls.

    After years of reading garbage code and dealing with garbage exception handling in everyone's code, especially mine, I wrote a wiki with do's and don'ts here on exception handling based on what I've learned working on OEM which is all java and I make all my team members read it. I've encountered lots of really badly written exception handling code for languages that make use of try...catch...finally concepts, and a basic set of rules makes life easier.  The same set of rules applies to C# code and ABL code.  

    In contrast my experience with functional programming is extremely limited.  I've read the articles on rust that was posted and I've tried to read a little bit of go.  I haven't studied either of these languages.  Java, javascript, and C# are my languages of choice and experience so anything I have to say about purely functional languages is worthless.

    Functional programming is hard.  I think I can safely say most developers come from the procedural or OO background.  From a learning perspective it is always good to read other code and understand the concepts and the experiments in language syntax.  You can then bend the concepts to your own work. 

    I think purely functional languages are fun...for a few minutes. Then I realize the language bends my mind so I can't figure out how to get anything done. Makes me wonder if the limited sets of libraries for some of these is directly caused by the lack of usability.  I can't say that the choice of exception handling in java and C# has improved the world, but it is an easily understandable language concept and works for more than the 5 of the top 6 most popular languages on earth.

    The way RUST enforces no-nulls and the resulting return value handling is neat.  I do kind of like it.  It isn't hard to understand or implement, while also forcing the developer to think about every case the compiler knows about.  The "error or value" baked into the language effectively uses an enumerated generic (I'm making this term up) and a not-null requirement combined with an enforcement of all cases in the enumerated type have to be handled.  You can emulate some of this in java or C# with a "holder" object and generics in OO languages and annotations. Java has @NotNull for parameter declarations.  C# has very similar syntax.  If you use them, the compiler will enforce them, the rest is just "don't throw an exception", rather use the holder object.  I think RUST solved it in a fairly clean way as it doesn't allow you to miss an exception case.

    At the end of the day if you pick SEH or enumerated return objects a few things go a loooong way, and I don't care what language constructs are enforced.  There are some class of things that defy the best language design.  A few below:

    1. The best exception handling is garbage if it doesn't do the right thing when one is encountered.  Your program may not crash, but you don't want an exception message box with "failed successfully" and no way to clear the error message.  No amount of language enforcement is going to prevent your program from doing stupid things if you don't know what you are trying to accomplish.  I've seen code (again and again) that does things like 'if a socket connection completely failed due to a bad hostname, the error message returned to the user would read "invalid username or password"'.

    2. Your program needs to return good error messages when there is no good thing left to do other than crash.  "save failed" type messages are useless.

    3. In most languages null pointer exceptions are a real thing. Except that fact unless you can use RUST or something similar.  No all of use can use a language that doesn't have them.  Use a linting tool in both your IDE and your build pipeline goes along way towards fixing our human short comings.  A few years ago when we introduced SonarQube to our build process.... my team had to fix 1600 potential null pointers in one project code base.  This was after years of manually looking for them and fixing them.  Thought we were doing to good.... :(

    4. All code is code reviewed (assuming you're not in your grandmother's basement by yourself...even then you can still ask) by more than one set of eyes.  We all do stupid things.

    5. Don't lose information when handling an error.  I've seen uncountable times when the real source of a problem is thrown away for a different error message.  don't do this.  preserve everything you can about the error until you are absolutely sure your program can recover or log the problem.

    6. Even unit tests are written incorrectly. Those get reviewed and linted and tested.  Turtles all the way down.

  • I'm with you on this on beavon.  I've seen cases where exceptions impact performance because they were being suppressed and the process repeated, but this is a rare case in my experience. If exceptions are used for cases where the program needs to get out usually the performance impact doesn't matter because you're aborting the process anyway. The overhead if you don't throw an exception is almost nil in most languages.  But you have to get it right and to do the unwinding of the stack correctly.  This is a skill that takes practice to learn how to do properly.  garbage collection helps with this and makes it easier.  null pointers being accepted in the language also make this easier.  But the rest is skill and program design to ensure your exception handling is side effect free.

  • >>> I think purely functional languages are fun...for a few minutes. Then I realize the language bends my mind so I can't figure out how to get anything done. Makes >>>me wonder if the limited sets of libraries for some of these is directly caused by the lack of usability.  I can't say that the choice of exception handling in java and C# >>>has improved the world, but it is an easily understandable language concept and works for more than the 5 of the top 6 most popular languages on earth.

      Maybe the only pure functional language is haskell. I have not learned it, so I cannot say much about it. Rust has functional features doc.rust-lang.org/.../ch13-00-functional-features.html, like golang. C# and java incorporated many functional concepts too. One of the problems that I have with OO is that it  makes it easy to shoot yourself in the foot, with inheritance, with something like throwing apperrors etc. Well designed languages protect you from shooting in your foot. You will have to struggle with the problem of backward compatiblity when developing/maintaining a language. While developing in a language like progress it is extra important to have a ruleset. Eschew inheritance, eschew throwing errors en much more. There is a lot to learn from programming in other languages, for progress developers the most from languages that are more unfamiliar than C# / java. Learn f.e. rust. No implementation inheritance,  good errorhandling principles and many new (functional) concepts that will astonish you. As a language with an excellent performance www.apriorit.com/.../520-rust-vs-c-comparison it might be an addition for your progress skills?

  • >>> I think purely functional languages are fun...for a few minutes. Then I realize the language bends my mind so I can't figure out how to get anything done. Makes >>>me wonder if the limited sets of libraries for some of these is directly caused by the lack of usability.  I can't say that the choice of exception handling in java and C# >>>has improved the world, but it is an easily understandable language concept and works for more than the 5 of the top 6 most popular languages on earth.

      Maybe the only pure functional language is haskell. I have not learned it, so I cannot say much about it. Rust has functional features doc.rust-lang.org/.../ch13-00-functional-features.html, like golang. C# and java incorporated many functional concepts too. One of the problems that I have with OO is that it  makes it easy to shoot yourself in the foot, with inheritance, with something like throwing apperrors etc. Well designed languages protect you from shooting in your foot. You will have to struggle with the problem of backward compatiblity when developing/maintaining a language. While developing in a language like progress it is extra important to have a ruleset. Eschew inheritance, eschew throwing errors and much more. There is a lot to learn from programming in other languages, for progress developers the most from languages that are more unfamiliar than C# / java. Learn f.e. rust. No implementation inheritance,  good errorhandling principles and many new (functional) concepts that will astonish you. As a language with an excellent performance www.apriorit.com/.../520-rust-vs-c-comparison it might be an addition for your progress skills? Maybe programming in some languages seems hard (at first). But over the years, when programming with "easy" languages that offer dangerous possibilities, you will (hopefully) discover, after many times shooting yourself in the foot, that programming with those languages is not as easy as you thought before.  Nice video on "easy": https://www.infoq.com/presentations/Simple-Made-Easy/