Page 1 of 1

Warnings

Posted: 03 Sep 2023, 02:05
by Descolada
This post is mostly a development of this post and thread.

First, for easier reference I'll copy my opinion about errors and warnings from that thread:
Concept-wise, in my opinion errors are serious problems in code from which the code cannot recover from on its own nor can continue without addressing the error. Warnings on the other hand are problems which *are* recoverable, or signal that under certain conditions (which may or may not have been met) the code might fail or cause problems. The current #Warn directives cause "continuable errors", which in my opinion is an oxymoron: it shouldn't be possible to continue after an error, these are in my opinion warnings.
Current status & issues
Currently there is no good way to *warn* about something when, for example, writing a library. It's possible to throw errors, but the default method of displaying them (dialogs) would generally not be suitable for warnings, especially for newer users who might not know how to redirect the errors into another modality. OutputDebug is one way, but it has the limitation of not reporting line number and file automatically, so it would fall to the library creator to always include those; and it can't easily be redirected (eg into a log-file) like is possible with errors (OnError).
Arguably another deterrent to actually using warnings in libraries/scripts is the waryness abot impact on script speed. AHK is known as a slow language already because of it's interpreted, and developers don't want to add extra "fluff" that could reduce script execution speed even further.

Default output
First I want to discuss the way warnings are and perhaps should be outputted to the used. At the moment we have only load-time warnings available via #Warn: VarUnset, Unreachable, and LocalSameAsGlobal. These, if met, by default issue continuable errors as a dialog (MsgBox). For VarUnset and Unreachable this seems fairly reasonable if accounting for beginner users, because these definitely help prevent errors down the line. This is especially true for VarUnset, which usually causes an actual error if not fixed.
However, LocalSameAsGlobal doesn't fit the same pattern: it might or might not prevent errors, but more likely reveals collisions with user-defined libraries, forces library writers to explicitly define all local variables as local to avoid the user getting a warning from common variable name collisions (i, j, out, etc...), and generally has a lower priority of taking action than the other ones. If LocalSameAsGlobal was also enabled by default this would most likely cause more annoyance at the warning pop-ups rather than help write better code. Thus LocalSameAsGlobal default output method should in my opinion be something more "silent" such as StdOut or OutputDebug.

Additionally, at the moment there are no runtime warnings, but if there were then it would probably be that some warnings are more important than others. For example, Python has deprecation warnings, which in AHK could be implemented for example in functions using RegisterShellHookWindow, which is flagged as deprecated by Microsoft. Warnings such as these should not throw dialogs at users either.

Best "silent" output method?
The question is which "silent" output method should be preferred? I argued in the other thread that OutputDebug seems best:
I also expect that users who know to enable warnings are also using software that allows for debugging, so OutputDebug seems like the best choice over StdOut or logging to a file.
But using StdErr might be preferred to keep errors/warnings separate from other debugging outputs?
Also, other programming languages tend to output their errors and warnings to a console of some kind, so either one wouldn't be straying far from the norm.
Another option would be logging into a log-file which would be more accessible to beginners (not requiring downloading a special debugger/console). But I would guess that people who are interested in warnings are also savvy enough to use an editor that supports debugging or StdErr outputting, so it wouldn't be my first nor second pick.

Additionally, if silent warnings were the norm then I think that *all* warnings could be enabled by default, including the loadtime warning LocalSameAsGlobal.


Possible syntax for warnings
lexikos wrote:
01 Sep 2023, 23:38
It might be useful to have a way to throw continuable errors as well, for example with throw Warning("This is continuable and creates a stack-trace like a normal error").
Throw is already continuable in v2.1-alpha.3, which I am preparing for release. As with all continuable errors, it is continuable only if try/catch is not in use.

Exceptions are thrown. Throwing a warning makes no sense. A warning just warns.
Okay, wouldn't make sense to throw something that isn't catchable. Perhaps a simple Log.Warn("Outputs line and file of where it came from?") is enough? The syntax would lend way to extend Log with other types of information as well.
The output method for runtime warnings could be changed via #Warn Runtime, WarningMode. And the default start settings could be:

Code: Select all

#Warn
#Warn LocalSameAsGlobal, OutputDebug/StdErr
#Warn Runtime, OutputDebug/StdErr
Also, runtime warnings should output only once by default: it is not necessary to see for example a deprecation warning every time the function is called.

Warning levels
Other languages often implement warning levels to separate less important warnings from more important ones, but at the moment the usefulness of those seem quite low as runtime warnings are not used yet and the need is not there. But if they were considered, I like how Rust separates types of debug information.

Script execution speed
Regarding the negative impact of the extra code on script execution speeds... I don't really see a way of conditionally "cutting out" the extra code without preprocessor directives. Currently the only way is if the script is planned to be compiled, then ;@Ahk2Exe-IgnoreBegin compiler directive can be used. Because preprocessor directive support has been in the talks for at least 15 years I assumed it's a lot of work to implement and thus suggested a simpler form in the other thread:

Code: Select all

#IfWarn
    Log.Warn("#Warn Runtime is off")
#If
but of course it would be preferred to have something like

Code: Select all

#define DEBUG
#IfDef DEBUG
    Log.Warn("Debugging is enabled")
#EndIf
or to make it more standard, #Warn Runtime could define WARN and then #IfDef WARN could be used instead.

Conclusion
All in all this seems like something that could be improved in AHK, but I'm not exactly sure of the exact way (or if at all? perhaps there is no interest in it). So I'm intrested in any thoughts about preferred output methods, necessity of warnings etc...

Re: Warnings

Posted: 03 Sep 2023, 03:49
by lexikos
Log is a math function.

Note that in the latest alpha, Warn?.(x) is equivalent to IsSet(Warn) && Warn(x), including the fact that x is not evaluated when Warn is unset.

If LocalSameAsGlobal was also enabled by default this would most likely cause more annoyance at the warning pop-ups rather than help write better code.
That's why it's turned off by default, although it's not just about annoyance. Showing a warning for something that is often completely benign (while also being virtually unavoidable) is misleading to beginners especially, even if it is in the editor's output pane rather than a dialog.
Thus LocalSameAsGlobal default output method should in my opinion be something more "silent" such as StdOut or OutputDebug.
That's not silent. I don't want this noise in my debugger output, nor do I want to be adding my own "default directives" to every script, as in v1.
forces library writers to explicitly define all local variables as local to avoid the user getting a warning from common variable name collisions
When you say it like that, I regret not removing the warning when I had the chance, rather than just pretending it doesn't exist.
#Warn Runtime
It definitely won't happen in that form. As you likely haven't noticed, v2.0 removed several directives that control runtime behaviour, replacing them with variables for flexibility.

Re: Warnings

Posted: 03 Sep 2023, 22:05
by iseahound
It's a meaningless distinction between warnings and errors as per your definitions.

To start off from a layperson's terminology, there are known unknowns and unknown unknowns. The average user interprets "mistakes" in their code as unknowns. By definition, if an error of an appropriate type can be raised, then it falls into the category of known unknowns, and therefore should be continuable.

An error that can be recovered is an error that can be recovered. If it cannot be recovered, then it will crash. In fact, it might be useful to display a warning that the script will crash. Similar to the blue screen of death. It's not necessary to reclassify warnings as errors or vice versa, the user knows what you mean regardless.

Now I don't know if you've programmed with friends, but no one pays attention to the red text warnings—spamming the user with useless notifications or text causes fatigue. The current actionable modal dialog is fine, it's intuitive, and if you don't like it, wrap all your code in a try block.