Open Screwtapello opened 5 years ago
@occivink on IRC mentioned an example like this:
try %{
fail error1
} catch error1 %{
fail error2
} catch error2 %{
echo -debug "???"
}
Would the above code echo ???
to the debug buffer?
If you translate the above code to Kakoune's existing syntax, then yes: each catch
block handles errors from the original try
block and all previous catch
blocks. This makes it easy to write code that tries a number of alternatives in order, without ridiculous nesting.
If you translate the above code to some other language with exceptions, like C++ or Python, then no: all the catch
blocks only handle errors thrown by the original try
block, not by any of the other catch
blocks. This makes it easy to write code that robustly handles specific errors, because you can be sure where a particular error came from.
On one hand, robust error handling is important for reliable, robust software, so I'm sympathetic to the idea that Kakoune should follow the model of other languages with exception handling.
On the other hand, literally no scripts in Kakoune's standard library check for specific errors at all today, so there doesn't appear to be a screaming need for rigorousness. Also, changing the exception-handling model that much would make migration from Kakoune's current syntax much more difficult.
Considering both options, I think it would be best to keep Kakoune's existing exception-handling model, and therefore I propose the example at the beginning of this comment should echo ???
to the debug buffer.
I agree with the proposal to make try
blocks more strict, and I think that try
blocks that doesn't have any catch
should be replaced with ignore_errors
block, while making try catch
more flexible solution for error handling, meaning that any try
must have at least onecatch .*
block.
ignore_errors %{
delete-buffer vaiv
}
Also I'd like to propose final
for handling errors that didn't matched any catch
:,
try %{
evaluate-command vaiv
} catch .*no\ssuch\scommand.* %{
...
} catch "some vaiv error" %{
...
} final %{
echo -debug "can't handle error"
}
however this would be a huge change, and to slim it down, catch
could take an optional argument, which means if no pattern specified, act like final
. This way it won't require any change to existing scripts.
This also involves the addition of throw
command, that will act like fail
but with a proper name
I'd like to propose
final
for handling errors that didn't match anycatch
IMO, final
, should be a clause that always executes, as it is in python. But I hardly see a need for that in Kakoune.
I think catching any error should just be catch %{...}
, and ignoring all errors should continue to just be try %{...}
with no catch clause, or try %{...} catch %{ nop }
if we want to make it more explicit.
I'd also like to point out that this feature would benefit from some form of standardization for error messages, to avoid making scripts more brittle.
Two solutions, off the top of my head, are:
literally no scripts in Kakoune's standard library check for specific errors at all today, so there doesn't appear to be a screaming need for rigorousness.
This is a sort of "innovator's dilemma". You are right that it means there isn't a pressing need. But, would we use the feature if it existed? Is no one checking for specific errors because it's not helpful? Or is it because the only current method is an awkward hack?
Currently, Kakoune commands can fail at runtime, either deliberately with the
:fail
command or when something unexpected happens, like when the<a-k>
("keep matching") command is given a regex that doesn't match any selection.Kakoune-script provides a way to handle such errors with the
:try
command. An error causes control flow to jump out of thetry
block and into the followingcatch
block (if any); an error in thecatch
block causes control flow to jump into the catch block after that, etc. This allows simple scripts to produce sophisticated behaviour, like the Use Tab for both indenting and completion snippet which uses:execute-keys
inside:try
to determine whether or not to set up mappings.Unfortunately, as in other programming languages, silently eating errors makes problems more difficult to debug than they should be. The
:execute-keys
command in that snippet is supposed to fail only when all cursors are preceded by whitespace, however it's easy to imagine a slight variant of the command that would fail due to a typo in the regex, or for some other trivial reason. Since:try
eats all errors, the snippet would silently do nothing, making it very difficult to debug.It is possible to have
:try
eat some errors but not others. Inside eachcatch
block, the%val{error}
expansion is set to the text of the error message, so you can do something like:This works, but:
.kak
files, so you'd have to see the reference to$kak_error
in the:try
docs and be familiar enough with Kakoune scripting to imagine the restfail
command instead of wherever it really came from.To fix this, I think the
catch
clause should work a little more like the:hook
command, having a regex parameter:When Kakoune is handling an error and reaches a catch block, it should match the error text against the block's regex, and only execute the block if a match is found. Like
:hook
, the regex must match the entire string, as if it were wrapped in^$
. If the error text does not match the regex, Kakoune should keep looking for the next catch block, hopefully with the original error line/column information. The%val{error}
expansion would still be available, just like%val{hook_param}
.One downside of this approach is that it would be incompatible with existing code that uses
catch
. If that's a concern, perhaps a different keyword could be used, likeexcept
, or it could be an optional flag, likecatch -matching some.*regex
Another downside is that makes Kakoune-script slightly closer to being a Real Language, instead of handling all control-flow in POSIX shell as intended. I think this is a reasonable expansion, since
:try
already exists, but I could be convinced the other way.