Null Coalescing Operator / Safe Navigation Operator

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

Null Coalescing Operator / Safe Navigation Operator

Post by iseahound » 14 May 2021, 22:41

Is there a need to introduce them to AutoHotkey? For those who don't know, the null coalescing operator will check if a value exists and evaluate it to return the left hand side, and a default value if null.

Code: Select all

obj := {a: 3}
MsgBox not obj.HasProp('b') ? "prop not found" : !obj.b() ? "evaluation is false" : obj.b()

obj := {a: 3, b: (*) => }
MsgBox not obj.HasProp('b') ? "prop not found" : !obj.b() ? "evaluation is false" : obj.b()


a := 3
MsgBox !IsSet(b) ? "var is unset" : not b() ? "evaluation is false" : b()

a := 3, b := () =>
MsgBox !IsSet(b) ? "var is unset" : not b() ? "evaluation is false" : b()
The above is too lengthy and could realistically be shortened to b ?? "default value".

Note that IsSet and HasProp do not evaluate, they only check to prevent errors. This is important because while HasProp and IsSet may return true, their evaluation may not.

However, since functions still return an empty string instead of unset, this is not as of great importance at the moment. (This may prevent proper chaining of functions such as a(b()) where a() uses IsSet() to define behavior.)

Code: Select all

MsgBox Type(fn()) ; String
fn() {
   return         ; "" empty string
}
Side Note: fn := () => () brings up a syntax error, I'm unsure if this is intended behavior.

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

Re: Null Coalescing Operator / Safe Navigation Operator

Post by Helgef » 15 May 2021, 00:41

You can use &&/||. Side note, yes it's intended behaviour.

SandyClams
Posts: 63
Joined: 02 Jul 2020, 11:55

Re: Null Coalescing Operator / Safe Navigation Operator

Post by SandyClams » 15 May 2021, 08:28

I feel your pain with the null checking vs. the truthy checking. One workaround I've considered implementing in my own code is to just have my utility functions return a reference to a custom "empty variable" object that I can then simplify the process of checking against as I continue in my own code. However, the divergence from intended usage is so radical in this case that I'm discouraged from actually doing it. I don't want to dissociate myself from how the things actually work or become dependent on some weird thing in my code style.

side note though @iseahound, you could at least simplify your checks some if you assigned some variables right there in the middle of your expressions. Well, you could reduce redundant calls, at least. Not sure if it's really "simpler" to have assignment expressions thrown around everywhere

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Null Coalescing Operator / Safe Navigation Operator

Post by swagfag » 15 May 2021, 09:22

sidesidenote, why does this:

Code: Select all

a := 3, b := () => 
MsgBox !IsSet(b) ? "var is unset" : not b() ? "evaluation is false" : b()
parse like this:

Code: Select all

a := 3, b := () => ''
MsgBox !IsSet(b) ? "var is unset" : not b() ? "evaluation is false" : b()
and not like this:

Code: Select all

a := 3, b := () => MsgBox(!IsSet(b) ? "var is unset" : not b() ? "evaluation is false" : b())
(in which case, yes, thered be nothing to call b() but thats irrelevant)

SandyClams
Posts: 63
Joined: 02 Jul 2020, 11:55

Re: Null Coalescing Operator / Safe Navigation Operator

Post by SandyClams » 15 May 2021, 09:48

@swagfag, I once wrote a v2 syntax definition for Sublime and I can tell you that newlines in expressions are hellish. In order to know what the above component means for sure, you need to check it against the value of the below component, but in that case the parsing "consumes" the below component by proceeding further and reading over it, which means, option A, you need a redundant backtrack just to go and categorize the original above element accurately, or option B, you're stuck giving the above component a "fuzzy", less precise definition that incorporates all the extra whitespace until the parser finds something that explicitly matches another discrete item. Only point being, I really can't blame Lexikos (presumably) for giving the parser some shorthand "rules" to interpret select patterns in certain ways rather than having it check those patterns more thoroughly in the roundabout fashions described, particularly with newlines

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

Re: Null Coalescing Operator / Safe Navigation Operator

Post by iseahound » 15 May 2021, 19:10

For reference, Helgef's suggestion is to use:

Code: Select all

var := IsSet(b) && b() || "default value"
The reason why I asked:

@SandyClams Yes, I feel compelled to use the old "" as a stand-in for the null parameter as unset requires more checks.

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

Re: Null Coalescing Operator / Safe Navigation Operator

Post by lexikos » 19 May 2021, 05:47

I will not add a null-coalescing operator while the language lacks null values.

I will not add null values in v2.0, but may reconsider for a future version.

See Nothing.


@swagfag
I assume that () => at the end of the line is parsed as a function having return with a blank parameter. It is not intended.

No other operator causes continuation if placed at the end of the line. Why would you expect => to?

Even with end-of-line continuation,

Code: Select all

b := () =>
MsgBox x
would never be parsed as

Code: Select all

b := () => MsgBox(x)
because => takes an expression, not a statement. It would instead be parsed as

Code: Select all

b := () => MsgBox x
where the function effectively contains return MsgBox x.

swagfag
Posts: 6222
Joined: 11 Jan 2017, 17:59

Re: Null Coalescing Operator / Safe Navigation Operator

Post by swagfag » 21 May 2021, 14:24

lexikos wrote:
19 May 2021, 05:47
I assume that () => at the end of the line is parsed as a function having return with a blank parameter. It is not intended.
No other operator causes continuation if placed at the end of the line. Why would you expect => to?
yeah, good question, i dont even know myself, really. since it wasnt throwing compile time errors, i guess it seemed more fitting that it should(catenate with the following line, that is)
the function effectively contains return MsgBox x
so, return the func reference to the MsgBox() function concatenated with the value of the (possibly global) variable x...

yikes

Post Reply

Return to “AutoHotkey Development”