Nothing.

Discuss the future of the AutoHotkey language
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Nothing.

Post by iseahound » 13 Sep 2018, 21:02

How would you handle division by 0? Currently it returns an empty string, so if implemented it should probably return nothing. I very much don't want to see an exception/error, much less different classes of errors, such as 1/0 = Infinity or even worse, 0/0 = NaN.

I just want to say I enjoy the simplicity of the current language, but would also like the benefits of stronger typing, which is necessary in my opinion for AutoHokey to evolve.

Guest

Re: Nothing.

Post by Guest » 13 Sep 2018, 23:25

division wrote:Division by zero causes an exception to be thrown.
:thumbup:

Cheers.

egocarib
Posts: 100
Joined: 21 May 2015, 18:21

Re: Nothing.

Post by egocarib » 30 Sep 2018, 11:15

While I can see the value of this change, I also don't fully understand all of the implications of it, which worries me a bit.

As someone who writes a lot of long and moderately complex AHK programs, and who works with others who are less experienced with the language but also use it a lot, it is extremely rare that I ever have worried about the ambiguity of "" in AHK. Really the only time I recall encountering issues with this is related to object keys, and in those cases it did not take long for me to understand what the limitations were and where I needed to be careful in order to ensure I don't introduce unexpected bugs. It is something that I rarely think about when programming in AHK.

On the other hand, a change like this makes me a bit nervous about whether we are heading to a landscape more similar to javascript, where testing of different "falsey" values is required in common workflows, forcing a larger number of users to worry about the differences between those various falsey values. For example, when I program in javascript, I regularly have to look up the differences between things like "", null, and undefined, and it is difficult for me to remember the difference between testing those values or where I need to worry about them. There are even different operators recommended to test them in different scenarios (== vs ===), etc.

I do see the usefulness of something like an "unset" value in AHK in certain scenarios, but I would advocate that we try and make sure such a change does not introduce unnecessary additional complexity into common workflows that less experienced users will have to deal with. I think there is a cost/benefit analysis that needs to be taken into account here.

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: Nothing.

Post by lexikos » 30 Sep 2018, 15:57

Thanks for the input, and let me reiterate:
lexikos wrote:No, this is not part of any plan. It is just a bunch of thoughts and hypotheticals that I wrote down in response to someone indirectly requesting null, as I wrote at the top.
No change happening here, just discussion.

User avatar
joedf
Posts: 8940
Joined: 29 Sep 2013, 17:08
Location: Canada
Contact:

Re: Nothing.

Post by joedf » 02 Oct 2018, 08:25

This is a non-existent issue... :HeHe:
Image Image Image Image Image
Windows 10 x64 Professional, Intel i5-8500, NVIDIA GTX 1060 6GB, 2x16GB Kingston FURY Beast - DDR4 3200 MHz | [About Me] | [About the AHK Foundation] | [Courses on AutoHotkey]
[ASPDM - StdLib Distribution] | [Qonsole - Quake-like console emulator] | [LibCon - Autohotkey Console Library]

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: Nothing.

Post by lexikos » 02 Jul 2022, 01:30

v2.0-beta.6 implements a few changes relating to this topic.
Changed Array to throw when retrieving an element with no value, consistent with Map, properties and variables.
Added UnsetError and reclassified MemberError, KeyError and IndexError.
Renamed KeyError to UnsetItemError and changed its message.

Added var? and unset for use with optional parameters.
Added var ?? value, mostly equivalent to IsSet(var) ? var : value.
Reading an unset or non-existent variable, property or map element already threw an error due to previous changes.

When I started this topic, I was only really thinking in terms of my prior experience with other languages:
  • Statically-typed languages where null is often treated like any other value (or isn't even technically a unique type).
  • Dynamic languages where variables can be any type, including null, which is again treated like any other value.
  • Dynamic languages where undefined variables silently return an "undefined" value.
In retrospect, I think we had already gained a sort of null value when #Warn was added back in 2011. Technically each variable has an additional flag indicating whether it is initialized, separate to its value; but that's mostly an implementation detail. From a different perspective, it can be seen as a null value, with the rule that attempting to read a null value...
  • in v1, implicitly converts it to "" with an optional warning
  • in v2, throws an error.
Because null is never directly given to the script to be used like any other value, whether it concretely exists as a value or within the type system doesn't really matter. Neither does the distinction of whether the language has null values or not, unless it helps to explain something.

v1.1.12 (2013) added the ability to omit a parameter by writing two consecutive commas, as in f(a,, b). Internally this is handled with the token type SYM_MISSING. For a long time I avoided providing any other way to produce this token, thus avoiding issues like in JavaScript, where f(v) acts as f() if v is unintentionally left uninitialized.

v2.0-a106 (2019) added IsSet(v), exposing the "nullish state" that variables start with.

v2.0-a112 (2020) added p:=unset for the parameter list of a function definition, as a very simple way to increase the flexibility of user-defined functions by taking advantage of IsSet.

Prior to v2.0-beta.6, I trialed () for explicitly omitting parameters, using load-time checks to try to avoid touching any broader nullish concepts and just solve the problem of conditionally omitting a parameter. Part of avoiding nullish concepts was restricting this to just parameter-passing. At first this seemed trivial, so this gave me the idea that an optional parameter variable could be permitted in that same context without sacrificing much error-detection; i.e. A(p:=unset) => B(p) could be equivalent to A(p:=unset) => isSet(p) ? B(p) : B(), while B(q) would be an error. However, the simple load-time checks turned out to be unreliable, not correctly identifying some expressions.

Next I trialed p?, and you can see the result in v2.0-beta.6. It is currently limited to the context of parameter-passing, but as [v] and {a: b} are really function calls, allowing it in those cases was also trivial - so I did, for flexibility. [v?] produces an array of length 1, where .has(1) may be true or false. {a: b?} produces an object which may or may not have an a property.

I then came to see that it was never really a question of whether to add null values, but whether or how to further extend their use in the language.



Possible future

This is how I can see it working:
  • A function, method or property can return unset - for instance, the __Item getter of Array or Map would do this instead of throwing UnsetItemError (taking care to differentiate between an undefined __Item property and an unset item, for error-reporting).
  • Any given operation evaluated as part of an expression may or may not expect unset. For instance, a[n]? and x.y ?? 'default' would expect it. Since those aren't implemented yet, everything implemented up until now would not expect it.
    • If it does not expect unset, an error is thrown immediately.
    • If it expects unset, that becomes its result, which is then passed to the next operation, and the process repeats.
  • The ? postfix operator would be valid only in specific contexts, such as in parameters or a direct assignment. Errors such as (a?) + b would be detected at load-time, since unset is never valid input for +. (AutoHotkey doesn't have any general mechanisms for type checking at load-time, but this could be described as such, with the types limited to "nullable" and "non-nullable".)
If you don't use ?, the behaviour is effectively the same as in current versions. The current implementation already uses a sort of unset result internally when invoking objects: INVOKE_NOT_HANDLED is returned in place of OK when a property or method doesn't exist. Whatever code that invoked it is then responsible for throwing an error if appropriate.



Functions

Currently functions return "" by default. I'm not sure that changing it at this stage is best, although it does seem that the use of an unspecified return value in an expression would more often be an error than intentional. Certain kinds of errors could be detected by flagging result := fn() as an error when there is no value (and no ? or ??).
I wrote:One of the problems with using "" as a default or null value is that if methods or properties are added to the String type (with either language changes or the default meta-function), these might be invoked accidentally when it would be more appropriate to throw a "no object" exception. For instance, myArray.length could be 0 when myArray is ""

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: Nothing.

Post by lexikos » 02 Jul 2022, 01:32

lexikos wrote:
06 Sep 2018, 04:08
C# also has null conditional operators, which produce null if the target object is null:

Code: Select all

int? count = customers?[0].Orders?.Count();
This seems a bit more challenging to parse due to ambiguity with ternary, unless the sequences ?[ and ?. are reserved for this (so a ? [ b ] : b and a ? .1 : .2 require spacing or parentheses).
JavaScript's "safe navigation operator" uses ?. even with [], as in arr?.[index]. However, JavaScript doesn't allow .1 for accessing properties; there's no need since [] is used for properties, not strictly collection items.


Helgef wrote:
07 Sep 2018, 12:54
array.key := unset and var := unset has a clear meaning
I think var := unset does. a := (b?) is also probably explicit enough. I experimented with this but didn't include it in v2.0-beta.6.

I think I was again trying to avoid nullish concepts, but that is making less sense after implementing p? and a ?? b. Scripts, including my own, were already relying on the initial unset state of variables via IsSet(), without much trouble. Maybe it doesn't make sense to withhold an easy way to reset the variable back to that state when it's so easy to implement. I was probably thinking that you can't set a variable to unset it, but we can say that x := y sets x while x := unset unsets it and x := (y?) either sets or unsets the variable.

However, array.key := unset might not work when key has a setter. value is an implicit mandatory parameter, and as such the setter cannot execute if this parameter is unset. It could be optional if we put it back at the end of the parameter list (a breaking change that increases complexity a little), or if we added the capability to place mandatory parameters after optional ones. Being optional would permit the setter to execute, and then I suppose any setter that doesn't expect an unset value would raise an error (because it's using value, not value? or ??).

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Nothing.

Post by Helgef » 04 Jul 2022, 02:57

However, array.key := unset might not work when key has a setter.
Perhaps it wouldn't call the setter, but the unsetter. Eg, defineprop {get,set,unset}. At least it wouldn't require any breaking changes.

Cheers.

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: Nothing.

Post by lexikos » 04 Jul 2022, 17:21

I guess I hadn't even considered making it a special case, invoking something other than the setter. Thanks.

iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

Re: Nothing.

Post by iseahound » 06 Jul 2022, 23:31

The context that I had in mind was actually working with values that may be initialized at some later point in time.
Example where a variable may or may not be initialized
I agree that having functions return unset and return x? should also be valid statements, and generally everything in the possible future section.

lexikos
Posts: 9560
Joined: 30 Sep 2013, 04:07
Contact:

Re: Nothing.

Post by lexikos » 07 Jul 2022, 05:39

iseahound wrote:
06 Jul 2022, 23:31
I agree that having functions return unset and return x? should also be valid statements,
I said nothing to suggest that return x? should be valid.

return x? cannot be a complete statement, because the expression would continue onto the next line.
The question mark must be followed by one of the following symbols (ignoring whitespace): )]},:.
Source: Variables and Expressions - Definition & Usage | AutoHotkey v2
Sometimes throws when Sleep is small (because cc is unset)
Good. What do you expect it to do when it has no value?
... working with values that may be initialized at some later point in time.
Variables, not values, and it isn't (and can't be) initialized at some later point in time. It must be initialized prior to the return statement evaluating it. If you were to use return (cc?), it would hypothetically return no value if cc wasn't initialized in time, and after that initializing it would have no effect.

I doubt that optional/unset return values are the proper solution to whatever problem you're thinking of.

Post Reply

Return to “AutoHotkey Development”