Skip to the content.

About 0.4.0

We leaped forward from 0.3 to 0.4 basically because the old error.handler is now on.error. I was working on a fix on error handling and took the liberty of changing the name to reflect a little better that it’s a kind of an event handler.

The fix

The fix itself is an interesting one: ExecLists used to share the same Process as the caller Command, so the error handler was shared between them both. That was a problem, because dealing with errors inside the ExecList’s SubProgram usually should be done differently than dealing with errors at the caller scope – even if, technically, they were sharing the same scope.

An example:

proc on.error (e) {
    print "e: $e"
    return "boom"
}
set x [invalidcommand]

As soon as the “command not found” error arises, on.error was being called directly from inside the ExecList. And that’s a problem, because the intuitive way of thinking is that the interpreter would search for on.error inside the ExecList’s SubProgram and, not finding it, would simply return as ExitCode.Failure and then the interpreter would search again, find it and call it in the “outer” scope. But that wasn’t what was happening, because both invalidcommand and set were, in this case, sharing the same Process.

What I did was to create a new Process for each ExecList evaluation, so the error handler search and Failure returns would follow the expected path.

I find interesting how, by facing this kind of situation, I understand much better why the POSIX shells do a lot of things the way they do.

What it “unlocks”

Now the programmer can (i) define an error handler inside the ExecList and (ii) even recover from errors by returning some value if he wants:

set x [ 
    proc on.error (e) {
        print "e: $e"
        return "boom"
    }   
    invalidcommand
]
print "x: $x"

Also, variables set inside an ExecList do not collide with variables from the caller scope. Yay! (But if you want to change something from outside, you can still rely on uplevel.)