wryun / es-shell

es: a shell with higher-order functions
http://wryun.github.io/es-shell/
Other
307 stars 25 forks source link

$&if does not catch exceptions in its conditions. #48

Closed memreflect closed 1 year ago

memreflect commented 1 year ago

The current behavior of $&if does not catch exceptions in its condition/test parameters:

# throws error $&access 'missing-cmd: No such file or directory'
if missing-cmd {
    echo command found
} {
    echo command not found
}

The description of the -e flag in the man page seems to suggest that this is actually an oversight/bug since the exception will cause the shell to exit in tests of conditional statements. Wrapping every single condition in a catcher works around this issue, but nobody wants to do that, right?

mwgamera commented 1 year ago

That would be quite confusing! No language with exceptions suppresses them in conditionals. The -e flag has nothing to do with exceptions but, like in other shells, makes it exit on non-zero exit status of a command, and this is exactly what the manual says about it. I can't see anything that would suggest otherwise. You don't need -e option for exceptions to terminate non-interacive shell; they always do when uncaught. I understand that your concern must be about the interactive shell that normally just prints out the value of uncaught exception and continues to accept input, but with the -e option it also terminates on exceptions of the "error" kind (everywhere, not just in the condition of if). Perhaps this is the part might need be changed or documented better, but I don't see any reason to do anything with $&if. Anyone wishing to simply suppress exceptions in if can easily do so by modifying $fn-if (although I don't think it's a good idea at all).

memreflect commented 1 year ago

In hindsight, i feel you are right. One should prefer notification of an exception in the script rather than having execution continue with unpredictable results, so it is actually the latter kinds of shells such as Bash that actually have the less preferable behavior, not es.

Anyone wishing to simply suppress exceptions in if can easily do so by modifying $fn-if (although I don't think it's a good idea at all).

It's definitely not a good idea when it breaks the shell (at least on my system), but that's an entirely separate issue, and i agree that it's a bad idea in general since it is so common.

Since i feel this issue is resolved, i am closing it. A catcher function like the following should be suitable enough for most purposes:

# catch error and signal exceptions in a command - for use in conditional contexts (if, while)
#
# result (error/signal exception thrown):
#    - a non-zero value to ensure the condition fails
#    - $estatus set to the exception thrown
# result (otherwise):
#    - whatever result would normally occur, including an uncaught exception
#    - $estatus undefined
#
# marked with $&noreturn to ensure things like <={- return 20} and `{- return 20} are uncaught by this lambda.
fn-- = $&noreturn @ cmd {
    estatus =
    catch @ e value {
        # re-throw all exceptions other than the ones we want caught.
        if {!~ $e error signal} {throw $e $value}
        estatus = $e $value
        result 111
    } $cmd
}