[REBOL] Re: Pre-REP on NONE! and SERIES! polymorphism
From: lmecir:mbox:vol:cz at: 14-Jun-2002 8:49
Hi all,
this is a thing loosely connected to the subject. I wrote a DEFAULT function
(may be of some use for error handling).
default: function [
{
try to evaluate code
and then evaluate the fault block,
if an error occurs
}
[throw]
code [block!]
fault [block!]
/good pass [block!]
] [result] [
either error? set/any 'result try code [
use [error] compose [
any-type? error: :result
do (reduce [fault])
]
] [
either good [do pass] [get/any 'result]
]
]
Here is a comparison of its behaviour with the functions contained in Core
2.5:
ex1: function [[catch]] [r] [
set/any 'r throw-on-error a
do b
]
ex2: function [[catch]] [r] [
if none? set/any 'r attempt a [
throw make error! "error"
]
do b
]
ex3: function [[catch]] [r] [
set/any 'r default a [throw error]
do b
]
a: [return "OK"]
b: ["KO"]
ex1 ; == "KO"
ex2 ; == "OK"
ex3 ; == "KO"
a: [()]
b: [if not value? 'r ["OK"]]
ex1; ** Script Error: blk needs a value
; ** Where: throw-on-error
ex2 ; == "OK"
ex3 ; == "OK"
The above tests show that both THROW-ON-ERROR as well as ATTEMPT should be
corrected. I suggest to write them as follows:
throw-on-error: func [
{Evaluates a block, which if it results in an error, throws that
error.}
[throw]
blk [block!]
][
if error? set/any 'blk try blk [throw blk]
get/any 'blk
]
attempt: func [
{Tries to evaluate and returns result or NONE on error.}
[throw]
value
][
if not error? set/any 'value try :value [get/any 'value]
]
Sometimes we need to distinguish between a pass value and a default value.
See the following example:
attempt [none] ; == none
attempt [1 / 0] ; == none
In the first case NONE is a pass value, while in the second one, it is a
default value. Compare that to:
default/good [none] ["default"] ["pass"]
default/good [1 / 0] ["default"] ["pass"]
This is an Achilles' heel of the ATTEMPT function and of any function that
uses NONE as a default value in the cases when NONE can be a pass value too.
The same problem exists with errors. An error can be a pass value as in:
a: head insert [] make error! "OK"
first a
; ** User Error: OK
; ** Near: a: head insert [error] make
, if A is a block containing an error value at its first position and it can
be a default value like in
1 / 0
; ** Math Error: Attempt to divide by zero
; ** Near: 1 / 0
We can distinguish these cases as follows:
error? first a ; == true
error? 1 / 0
; ** Math Error: Attempt to divide by zero
; ** Near: error? 1 / 0
The TRY function cannot discern these cases and therefore we do not have any
general function able to do that. The DEFAULT function isn't working as I
would like it to work, because it uses the TRY function.
A few questions:
- Should Rebol have a native version of the DEFAULT function able to discern
the default and pass cases?
- Should the behaviour of error values change a bit to enable the
interpreter to yield errors as pass values?
- Should we have versions of PICK, SELECT, etc. functions discerning the
pass and default cases?
An unrelated note: The usage of NONE in place of an empty block is not a
Rebol rule. There are cases, when Rebol returns an empty block:
exclude [a] [a b] ; == []
-L