Should EVALUATE bomb on an error?

Currently if you try something bad in EVALUATE, it throws an error:

>> evaluate [unspaced null]
** Script Error: unspaced requires line argument to not be null

As EVALUATE is a relatively low-level function, it would seem more likely one would want to handle the error on the same basis as other possible return values:

; assuming EVALUATE returns [product position]
>> evaluate [unspaced null foo bar]
== [**unspaced-null [foo bar]]

In this case, the bomb isn't particularly informative and seems reasonable to say 'user beware—assume errors will happen'. It's kind of difficult to work around too.

This sort of puts it in the same class as TRAP with different semantics:

trap [ok ok something bad] => [**something-bad null]
trap [ok ok] => [null ok]
evaluate [something bad ok ok] => [**something-bad [ok ok]]
evaluate [ok ok] => [ok [ok]]

I guess the wrinkle here is how do you determine where something bad ends and ok ok resumes? That may or may not be obvious.

1 Like

Rebol can't measure the span of a single step of evaluation without having the side-effect of running it. That's just the nature of the beast.

I'd once tried making a "neutral" mode of the evaluator which would only gather arguments but not have any side effects. This would be able to count through the function arguments, and the arguments to functions that were those arguments, and so on:

 >> eval-neutral [print "Hi" print "Bye"]
 == [print "Bye"]   ; no actual printing done, but arity of PRINT exploited

 >> eval-neutral [print "Bye"]
 == []

But this falls down the moment you run code which changes the definitions:

 >> redefine-print: func [] [print: does [print "PRINT is arity-0 now"]]

 >> eval-neutral [redefine-print print "Bye"]
 == [print "Bye"]  ; didn't actually *run* REDEFINE-PRINT

 >> eval-neutral [print "Bye"]
 == []  ; should have only stepped past PRINT, leaving "Bye"

Some aspect of this foundational problem applies any time you try to resume things. Hence, the only granularity of resumption can be the end of BLOCK!/GROUP!.

(It's this "we can't know the limits of boundaries of expressions" that tripped up the idea of making the MATH dialect for precedence reordering able to mix expressions without putting all executable expressions between the operators in GROUP!s.)

I've written some ideas on making a more formal contract between callers and things that error. Perhaps you would be able to weigh in there: