The historical Rebolism of THROW and CATCH has nothing to do with error handling.
It's a generic way to move values up the stack. It gives you a handy "out" from control flow:
result: catch [
if condition [throw result]
some code
case [
condition [more code]
condition [more code, throw result]
]
additional code
throw result
]
The implementation of the feature is lightweight, and built on the same mechanic as RETURN. You could in fact use return to do this:
result: do func [] [
if condition [return result]
some code
case [
condition [return code]
condition [more code, return result]
]
additional code
return result
]
But that's more heavyweight, because it gets binding involved (although I've suggested that maybe CATCH and THROW should be definitional, and the "heaviness" is a feature... not a bug).
It's A Neat Feature, But I Want THROW and CATCH For Errors
Error handling is undergoing a renaissance in Ren-C, and it's becoming completely pervasive.
Despite its glory, it is currently tied in with the junky names RAISE and EXCEPT.
case [
...code that may fail...
] then [
...stuff to do if non-NULL, non-VOID, non-RAISED error!...
] else [
... stuff to do if NULL or VOID or RAISED error!...
] except e -> [
... error handling ...
]
The word EXCEPT is bad for several reasons:
-
It implies "exception" handling, which definitional errors are specifically not...they can only be caught one stack step at a time.
- Arbitrary exception handling is taboo for very good reasons...and I've made it more taboo by moving that behavior to SYS.UTIL.RESCUE
-
It's not really a "verb"
-
It often comes right after an ELSE, and I don't like the alliteration of E and E
-
Almost every other language uses CATCH there
And RAISE isn't exactly my favorite word either. return throw e vs. return raise e also has that alliteration issue.
So What Can The Old THROW and CATCH Be Called?
It's a lot less important than the error handling application, but I still use the old construct.
You can actually do it with CYCLE and STOP:
result: cycle [
if condition [throw result]
some code
case [
condition [more code]
condition [more code, stop result]
]
additional code
stop result
]
Being a looping construct, it has the side effect that it will keep repeating unless a value is emitted.
On the plus side...this helps resolve the semantic question of "what should a CATCH return when there's no THROW?" If what you have is CYCLE then your answer is that it keeps going. So you either put a FAIL at the bottom, a STOP at the bottom, or accept it will keep going.
On the minus side...if you use this inside a looping construct you'll be redefining BREAK, which historical CATCH would not do. (Similarly, if you use FUNC and RETURN to do this you'd redefine RETURN.)
With definitional BREAK and RETURN there'd be workarounds for those cases--you'd simply give another name to the outer break and return if you needed them. But that's not how things work today.
While CYCLE is Not Perfect, It Would Work For The Moment
We can meditate on what the ultimate answer would be. But there's only a few cases, and CYCLE will do.
Errors need THROW and CATCH...they're now too fundamental to not use the good words for.
We now have the word TRAP for those who don't want to use enfix, so these are equivalent:
if e: trap [some code] [
handle e
]
(some code) catch e -> [
handle e
]
But you get some real benefits from the THEN + ELSE + CATCH, and I think the importance of these enfix constructs will become apparent to those who aren't on the bandwagon (yet).