Try-Catch In UCL - Some Notes
UCLStared working on a try
command to UCL, which can be used to trap
errors that occur within a block. This is very much inspired by
try-blocks in Java and Python, where the main block will run, and if any
error occurs, it will fall through to the catch block:
try { echo "Something bad can happen here" } catch { echo "It's all right. I'll run next" }
This is all I've got working at the moment, but I want to quickly write some notes on how I'd like this to work, lest I forget it later.
First, much like everything in UCL, these blocks should return a value. So it should be possible to do something like this:
set myResults (try { result-of-something-that-can-fail } catch { "My default" }) --> (result of the thing)
This is kind of like using or
in Lua to fallback to a default, just
that if the result fails with an error, the default value can be
returned from the catch
block. In might even be possible to simply
this further, and have catch
just return a value in cases where an
actual block of code is unnecessary:
set myResults (try { result-of-something-that-can-fail } catch "My default")
One other thing to consider is how to represent the error. Errors are
just treated out-of-band at the moment, and are represented as regular
Go error
types. It might be necessary to add a new error
type to
UCL, so that it can be passed through to the catch
block for logging
or switching:
try { do-something } catch { |e| echo (cat "The error is " $e) }
This could also be used as the return value if there is no catch
block:
set myResult (try { error "my error" }) --> error: my error
Another idea I have is successive catch blocks, that would cascade one after the other if the one before it fails:
try { do-something } catch { this-may-fail-also } catch { echo "Always passes" }
Unlike JavaScript or Python, I don't think the idea of having catch
blocks switching based on the error type would be suitable here. UCL is
dynamic in nature, and having this static type checking feels a little
wrong here. The catch
blocks will only act as isolated blocks of
execution, where an error would be caught and handled.
Finally, there's or
0, which would run regardless of which try or catch
block was executed. I think, unlike the other two blocks, that the
return value of a or
1 block will always be swallowed. I think this
will work as the finally block should mainly be used for clean-up, and
it's the result of the or
2 or or
3 blocks that are more important.
set res (try { "try" } catch { "catch" } finally { "finally" }) --> "try"
Anyway, this is the idea I have right now.
Update — I just realised that the idea of the last successful try block return an error, rather than letting it climb up the stack defeats the purpose of exceptions. So having something like the following:
try { error "this will fail" }
Should just unroll the stack and not return an error value. Although if there is a need to have an error value returned, then the following should work:
try { error "this will fail" } catch { |err| $err } --> error: this will fail