So here is some code for CATCH being used to implement QUIT:
catch [
set (extend mod 'quit) make-quit throw/
wrap* mod body ; add top-level declarations to module
body: bindable body ; MOD inherited body's binding, we rebind
eval inside mod body ; ignore body result, only QUIT returns value
throw ~
]
then ^arg-to-throw -> [ ; THEN with ^META receives ERROR!, PACK!, VOID!
ignore ^product: ^arg-to-throw
]
Our default assumption is that you don't want a script to actually return the INTEGER! of 1 when you say quit 1. So MAKE-QUIT is used to create a function that will wrap the THROW with behavior to turn it into a function that will return an ERROR! instead when given an integer (it turns quit 0 into just a TRASH!, as opposed to an error holding an exit code of 0...the same thing you get if a script exits normally.)
But a script can actually return anything when invoked with DO, so there's a refinement to flip the interpretation, e.g. quit:value 1 means "I actually want this script to synthesize the value 1".
All That Aside, Let's Talk Results
Because the above code uses THEN it means it assumes that "light null" is reserved for the case where you don't THROW.
In that presumption, you can't throw null and expect to run an ELSE.
catch [
throw null
] else [
; this would not run, under the assumption
]
This concept arose from the idea that being able to tell whether you did THROW or not was important. But distorting the result in a non-branching structure is probably not ideal.
Then...if CATCH returned its body result, this could be instead:
ignore ^product: catch [
set (extend mod 'quit) make-quit throw/
wrap* mod body ; add top-level declarations to module
body: bindable body ; MOD inherited body's binding, we rebind
eval inside mod body ; ignore body result, only QUIT returns value
~ ; body result
]
There's some echo of the issues with RETURN, of whether or not to put an implicit call to the QUIT function to allow people to hook it and be guaranteed it would run on exit. (At the moment, RETURN is not run implicitly.)
While I'm not entirely sure what I think here, I do wonder if it's over-reaching to box nulls as the built-in THROW behavior.
catch [
throw null
] else [
print "This ELSE would run..."
]
If you want to make your throw "heavy" you can do so:
catch [
throw: throw/heavy/
throw null
] then [
print "This THEN would run..."
]
The reason the initial choice was made was so that the [CATCH...THEN] read as "if a throw occurred that you could catch, then..."
But QUIT doesn't want it, because that linguistic play on "CATCH" doesn't apply. I don't know that in the grand scheme of things it's worth building-in the distortion, as opposed to saying it's [CATCH...a non-NULL throw THEN...].
![]()
UPDATE: I realize that if this is what you want to do, then there is the ATTEMPT construct... that is designed for the purpose of being able to either BREAK or CONTINUE, where BREAK will signal an ELSE and CONTINUE will signal a THEN!
https://rebol.metaeducation.com/t/repeat-1-attempt-not-as-useless-as-it-looks/2480