I'm a bit confused over error handling in Rebol. It has THROW and CATCH constructions:
>> repeat x 10 [if x = 9 [throw "Nine!"]]
** Throw error: no catch for throw: make error! 2
>> catch [repeat x 10 [if x = 9 [throw "Nine!"]]]
== "Nine!"
But this throwing and catching is unrelated to the handler passed to the /EXCEPT refinement on TRY:
>> try/except [throw "Nine!"] [print "Exception handled!"]
** Throw error: no catch for throw: make error! 2
That's very specific to Rebol's error type:
>> try/except [print 1 / 0] [print "Error handled!"]
Error handled!
If you want you can trigger your own errors, but not using THROW as you would in other languages. Throwing errors will just lead to a complaint about not being caught, as any other value type:
>> try/except [throw make error! "Error string"] [print "Error handled!"]
** Throw error: no catch for throw: make error! 2
You must DO them to get the evaluator to try and execute something of type error! to cause what it considers an "exception":
>> try/except [do make error! "Error string"] [print "Error handled!"]
Error handled!
(Note: You can use pre-made errors apparently such as cause-error 'Script 'invalid-type function!
-- see system/catalog/errors
for more.)
Leaving off the /EXCEPT refinement will let you receive any errors as a value. However, that seems to make it impossible to distinguish between whether the error was invoked or not:
>> probe try [do make error! "Some error"]
make error! [
code: 800
type: 'User
id: 'message
arg1: "some error"
arg2: none
arg3: none
near: none
where: none
]
** User error: "Some error"
>> probe try [make error! "Some error"]
make error! [
code: 800
type: 'User
id: 'message
arg1: "Some error"
arg2: none
arg3: none
near: none
where: none
]
** User error: "Some error"
It would seem that CATCH has a similar lack of distinction between returning a value and having a value thrown. But there's a tool to get around that with "named throws":
>> code: [repeat x 10 [if x = 9 [throw/name "Nine!" 'number]]]
>> catch/name [do code] 'number
== "Nine!"
>> catch/name [do code] 'somethingelse
** Throw error: no catch for throw: make error! 2
So now for the questions:
Is there a practical value to this separation? How does one decide whether to use THROW and CATCH or a DO of an error and handle it with TRY/EXCEPT?
Does this distinction have precedent in other languages, and would /EXCEPT be better named /ON-ERROR or something?
Why does it say "no catch for throw: make error! 2"
instead of something more informative? What is the make error! 2
?
"Is there a practical value to this separation? How does one decide whether to use THROW and CATCH or a DO of an error and handle it with TRY/EXCEPT?"
As has been pointed out in the other answers as well, THROW and CATCH form an unwinding construct (a non-local exit) that in and of itself is concerned with control flow and doesn't necessarily have anything to do with error handling. THROW and CATCH is first and foremost a straightforward method to influence control flow and is a rather fundamental building block for other custom control flow constructs.
As such, THROW and CATCH can of course also be used for building an "exception handling"-like error system as seen in many contemporary mainstream languages, because those "exception handling" systems are one instance of non-local control flow.
Rebol's error!
on the other hand is the primary method to signal and propagate evaluation errors.
So generally, the decision is easy to make: if you want to cause an error, use error!
. If you want to influence control flow to unwind controllably, use THROW / CATCH.
Two more remarks regarding terminology:
"Does this distinction have precedent in other languages"
Yes, the distinction between evaluation errors and non-local exits has precedent in other languages, especially the Lisp family. A few quick references:
catch
and throw
vs condition signaling
catch
and throw
vs error signaling
block
vs the condition system
"Why does it say "no catch for throw: make error! 2" instead of something more informative? What is the make error! 2?"
That's a bug. (Good catch!) I'd say that the core of the error message ("no catch for throw") is rather informative already, but the make error! 2
is a bug (it should rather show the thrown value here.)
"would /EXCEPT be better named /ON-ERROR or something?"
Renaming /EXCEPT
is debatable. As such, I'd say this is a discussion not that suitable for SO Q&A and better left to other fora.
I would say that the regular throw is not for error handling, but for shortcuts or goto. It serves something as a break in loops etc. You could use it also to mimic a continue
See is-there-an-equivalent-to-continue
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With