Four Js Development Tools Forum

Discussions by product => Genero BDL => Topic started by: Eric Byrnes on January 25, 2021, 11:37:05 PM

Title: Language Modernization ideas for discussion - Idea 2 - User-defined exceptions
Post by: Eric Byrnes 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 (genero) Select
PRIVATE DEFINE e_PrivateBadStuff EXCEPTION
PUBLIC e_PublicBadStuff EXCEPTION

PUBLIC DEFINE e_RowLocked EXCEPTION -244

FUNCTION test()
   DEFINE e_LocalBadStuff EXCEPTION

   TRY
      IF myBadStuff() THEN
         RAISE e_LocalBadStuff
      END IF
   CATCH e_RowLocked
      DISPLAY "Next row is locked!"
   CATCH e_LocalBadStuff
      DISPLAY "Something bad happened!"
   FINALLY
      ROLLBACK WORK
   END TRY
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.
Title: Re: Language Modernization ideas for discussion - Idea 2 - User-defined exceptions
Post by: Reuben Barclay 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 (genero) Select
   
TRY
    DISPLAY 1/0
CATCH
    CASE STATUS
        WHEN -1202
            DISPLAY "Divide by zero error"
        OTHERWISE
            DISPLAY "Some other error"
    END CASE
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 (genero) Select
   
TRY
    DISPLAY 1/0
CATCH -1202
    DISPLAY "Divide by zero error"
FINALLY
    DISPLAY "Some other error"
END TRY 



Reuben
Title: Re: Language Modernization ideas for discussion - Idea 2 - User-defined exceptions
Post by: Eric Byrnes 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