R3-Alpha's TRY added a refinement /EXCEPT for passing in a block or function to act as a handler in the case of an error being raised. It struck me as a clunky attempt to parrot existing terminology.
TRAP was created to be a seemingly better name than "TRY". It appeared to have more parity with CATCH, and TRAP/WITH paralleled CATCH/WITH.
I think the name is an improvement. And it paved the way for the short word TRY to fill another important role.
But there's a pattern in both of these constructs which is that they return a result whether something is caught or trapped or not.
>> catch [10 + 20]
== 30
>> error? trap [make error! "this is *not* a trapped error"]
== #[true]
That particular behavior of trap is particularly tricky because many cases check if a result is an ERROR? and use that as a detection of if a FAIL ran...and here we see that's not actually happening.
So if you're trying to write truly "correct" code that uses a TRAP or a CATCH, you pretty much wind up writing a TRAP/WITH or a CATCH/WITH...providing a function taking an error.
But I've been questioning the value of this mixing-up-of-return-results. If you want to get a value out of the block, why not do that by setting a variable? You're usually trying to set a variable anyway, e.g. value: trap [...]
, what's wrong with moving it into the code?
trap [
value: some-calculation-that-may-fail ...
] then func [e] [
... code to handle the error ...
] else [
... stuff to do if there was no error ...
... assume value is good ...
]
This cleanly separates out the code paths, allowing usage of null-sensitive constructs. So it means getting rid of the /WITH refinement on TRAP and CATCH, instead using normal THEN/ELSE/etc. constructs with them:
>> catch [10 + 20]
== ~null~ ; anti
>> trap [make error! "not a thrown error"]
== ~null~ ; anti
In the case of CATCH, you can always just throw your final result, to get it to conflate with an ordinary throw (this is inexpensive.)
TRAP can't do that (since it would only be able to return errors). Though you could piggy-back on CATCH if you really wanted to avoid variable declaration with a TRAP...just throw your result:
catch [trap [... throw result] then e -> [e]]
I'm not opposed to the idea of code-golf-friendly constructs which could go ahead and do this squashing of results together. (CATCH-DO, TRAP-DO?) But the clean expression with only returning the caught or trapped thing--and null otherwise--seems quite appealing to me for the primitive building block.
Also note: the existence of ENTRAP
I made ENTRAP to address the problem of distinguishing errors from other values, by returning the meta form of the value in the case of no errors. So if your result is QUOTED! or QUASIFORM!, then you know it succeeded and all you have to do is unmeta it to get the result. ERROR! is the only plain value returned...so it is an unambiguous signal of a trapped failure.
>> entrap [null]
== ~null~
>> entrap [10 + 20]
== '30
>> error? entrap [1 / 0]
== ~true~ ; anti (it's just a plain ERROR! value, not quoted or quasi)
>> quoted? entrap [make error! "abc"]
== ~true~ ; anti (quoted error, since it wasn't RAISE'd)
I don't know how that fits into the naming and scheme of things, but mentioning it.