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
.)