Should EVALUATE/NEXT bomb on an error?

We can't to do anything about hard failures (which can occur anywhere in the middle of an incomplete expression, at any stack level, even from just looking up a word that's a typo). No hope of graceful handling there...

...BUT we can theoretically do something about the new and novel definitionally raised error antiforms :atom: which emerge from the overall step, that have not yet been promoted to a failure. Because the antiform ERROR! is a legitimate evaluation product, and the flow of control has not yet been interrupted.

(And luckily, all meaningfully interceptible errors are definitional. Read the above link to understand why.)

Though It Turns Out To Be Tricky. :thinking:

EVALUATE/NEXT gives back a multi-result pack, with the position as the first pack item, and the synthesized value as the second.

But we don't want raised errors in PACK. In fact, if a function returns a raised error... that's the only thing it can return. Because instead of the antiform BLOCK! (the pack of values) you're returning an antiform ERROR!.

So EVALUATE/NEXT can't give you back both a raised error and a position.

Or...could it?

The expression completion position could be a field in the error itself.

Using some overlong descriptive names to illustrate:

[pos value]: evaluate/next [1 / 0 10 + 20] except e -> [
     if e.id = 'raised-error-in-evaluate-next [
         assert [e.error.id = 'divide-by-zero]  ; actual error is wrapped in e
         pos: e.resume-position  ; e.g. [10 + 20]
     ] else [
         fail e  ; some other error
    ]
]

There are more mundane approaches, such as adding /EXCEPT such that EVALUATE/NEXT/EXCEPT produces a ~[pos value error]~ pack instead of just a ~[pos value]~ pack. Then you have to remember to check that the error is not NULL on all paths. That sounds less foolproof.

Another trick could be to have an /ENTRAP refinement. The concept behind ENTRAP is to take everything up one meta level...

>> entrap [10 + 20]
== '30

So 10 + 20 gave you a quoted 30. And if you had a plain ERROR! you would get a quoted error. If you had a null antiform you'd get a quasi-null.

>> entrap [pick [a b] 3]
== ~null~

This means all values will be metaforms... either quoted or quasi.

But then, if an ERROR! antiform is encountered... ENTRAP returns it in a plain form:

>> entrap [1 / 0]
== make error! [
    type: 'Math
    id: 'zero-divide
    message: "attempt to divide by zero"
    near: '[1 / 0 **]
    where: '[/ entrap console]
    file: ~null~
    line: 1
]

And it's the only plain form you can get. So if you get a plain ERROR? RESULT back from EVAL, you know it actually represents a raised one. Otherwise your real result is the UNMETA of what you got (drop a quote level from quoted things, turn quasiforms into antiforms).

(It's a weird multiplexing trick, but it's serviceable...and kind of a testament to the versatility of the isotopic model.)

So there's hope on this! I'm actually working on something that needs this right now. Because without something like this, you cannot write TRAP in usermode...