Subscribe for automatic updates: RSS icon RSS

Login icon Sign in for full access | Help icon Help
Advanced search

Pages: [1]
  Reply  |  Print  
Author Topic: Language Modernization ideas for discussion - Idea 2 - User-defined exceptions  (Read 10705 times)
Eric B.
Posts: 10


« on: January 25, 2021, 11:37:05 pm »

Continuing the language modernization ideas thread set: User-defined exceptions.

Completely ripped off from other languages.  The declaration would be similar to the syntax of a variable definition with a type of EXCEPTION, including an optional status code to map a STATUS value to an exception.  There could be multiple CATCH clauses.  The CATCH clause would accept an optional defined exception name or system status code; if no name was provided, it would capture any generic exception not previously matched.

A status value-based exception, like e_TableNotFound below, would be triggered whenever a statement generated the given status.  A user-defined exception would be triggered using RAISE.

Bonus points for adding a FINALLY clause (although I guess it’s possible to work around that)!

For instance:
Code
  1. PRIVATE DEFINE e_PrivateBadStuff EXCEPTION
  2. PUBLIC e_PublicBadStuff EXCEPTION
  3.  
  4. PUBLIC DEFINE e_RowLocked EXCEPTION -244
  5.  
  6. FUNCTION test()
  7.   DEFINE e_LocalBadStuff EXCEPTION
  8.  
  9.   TRY
  10.      IF myBadStuff() THEN
  11.         RAISE e_LocalBadStuff
  12.      END IF
  13.   CATCH e_RowLocked
  14.      DISPLAY "Next row is locked!"
  15.   CATCH e_LocalBadStuff
  16.      DISPLAY "Something bad happened!"
  17.   FINALLY
  18.      ROLLBACK WORK
  19.   END TRY
  20. END FUNCTION

I think this would greatly improve readability, familiarity to developers of other languages, reduce the temptation toward complicated CASE and IF nesting scenarios, and help clean up 4GL’s less-than-wonderful error handling patterns.
Reuben B.
Four Js
Posts: 1119


« Reply #1 on: January 26, 2021, 02:41:19 am »

I think there are two issues here.

1. Is the ability to throw a user-defined exception.  This has been discussed previously https://4js.com/support/issue/?id=FGL-4064 and whilst not ruled out entirely, it was said that if this was going to happen the keyword would probably be THROW and not RAISE, presumably to avoid confusion with the existing WHENEVER ANY ERROR RAISE.  It would require an option to populate SQLCA.SQLERRM so something like

THROW -9000 WITH "My exception text"

Part of the reasoning that saw this not implemented was consider case of CALL foo(bar()), if you throw exception in bar() should foo() be evaluated?

However looking at your example, I wonder if we allowed THROW only inside a TRY, would that alleviate our concerns?  Were you expecting it to be used outside of a TRY?



2. Having CATCH handle different exceptions and also have a catch-call FINALLY .  This can be achieved simply by doing a CASE STATUS as first line of CATCH e.g.

 
Code
  1.  
  2. TRY
  3.    DISPLAY 1/0
  4. CATCH
  5.    CASE STATUS
  6.        WHEN -1202
  7.            DISPLAY "Divide by zero error"
  8.        OTHERWISE
  9.            DISPLAY "Some other error"
  10.    END CASE
  11. END TRY  

you could argue that internally the compiler could convert the following to that.  Is the additional syntax necessary if you adopt the above pattern?

 
Code
  1.  
  2. TRY
  3.    DISPLAY 1/0
  4. CATCH -1202
  5.    DISPLAY "Divide by zero error"
  6. FINALLY
  7.    DISPLAY "Some other error"
  8. END TRY  


Reuben

Product Consultant (Asia Pacific)
Developer Relations Manager (Worldwide)
Author of https://4js.com/ask-reuben
Contributor to https://github.com/FourjsGenero
Eric B.
Posts: 10


« Reply #2 on: January 26, 2021, 09:51:15 pm »

Reuben,

I agree THROW makes more sense and the prospective syntax you provided meets the spirit of the request.  I see your point on the CASE vs multiple CATCH clauses - the multiple CATCHes seem syntactically cleaner to me but since CASE achieves the objectives, I'd be good with that.

Regarding exception propagation, I'd personally want to allow a THROW anywhere, including outside a TRY.  I would think the control flow behavior would be whatever would otherwise happen with a system exception based on the WHENEVER in effect - if RAISE, exit the function and propagate to the parent, if CONTINUE, continue, if STOP, stop, and so on.  The behavior of your example of CALL foo(bar()) might vary based on that, but for RAISE or STOP, it would NOT call foo in that case because execution within bar() would stop without a return value.  For continue, the THROW would have no effect other than to set the status, so it WOULD call foo, and for CALL it would call the designated function, then continue and end up calling foo as well.  At least, that's how I would view it.

The intent of FINALLY is to imitate the Java behavior which is a bit different from what you described.  FINALLY always executes at the end of the TRY block, not just when there is an exception - that code is guaranteed to execute before leaving the TRY block, regardless of what happens - exception, no exception, RETURN in the TRY block, whatever - and it's value is in that guarantee.  Truly imitating that behavior would require multiple additional statements, not just a CATCH case with an OTHERWISE.

Thanks for considering this!

Eric
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines