[Wish] Try() Expression returning Unset

Discuss the future of the AutoHotkey language
sirksel
Posts: 222
Joined: 12 Nov 2013, 23:48

[Wish] Try() Expression returning Unset

05 Sep 2023, 08:31

Over the past years of 2.0, the general trend seems to have been to eliminate the empty-string error returns in favor of explicit Error throws. I agree with that. Explicit is usually better than implicit. That said, it seems like we do lose a little of the quick-and-dirty scripting efficiency over time, as the errors typically need to be handled.

For example, I do have occasion to want to write a quick one-liner function, whose failure for any reason should return as some default value. Usually that's an empty string. Sometimes it's 0 or a null sentinel. With some of these recent changes to allow unset returns, I'm thinking I will modify those null sentinel instances to return unset (if I can figure it out), since it works so nicely with the maybe operators. Currently anyhow, fwiw, my trivial implementation looks like this:

Code: Select all

tryd(fnc, dft) {              ;try or default val
  try return fnc()
  return dft
}
trye(fnc) => tryd(fnc, '')    ;try or empty string
tryz(fnc) => tryd(fnc, 0)     ;try or zero
Then in a one-liner method or callback function with some calc which may fail, but I don't care about the reason, it looks like this:

Code: Select all

calc(other) => tryz(()=>(this/other))
In the same way you made Throw() a function that can now be used in an expression, it might be useful if Try could get a similar treatment for expressions? I propose that it would be the same as the try with no catch, but return unset? It could enable the following:

Code: Select all

calc(other) => try(this/other)          ;if you want an unset return on failure
calcz(other) => try(this/other) ?? 0    ;or if you want a 0 return
Not sure how hard that would be to implement, since you've already got the maybe monad infrastructure and operators so well fleshed out. It would definitely improve efficiency in places where full error handling isn't necessary. With the new ?? structure, it could be equally as efficient as the old empty-string returns, but much clearer and more explicit.

And thanks so much, @lexikos, for all the great work so far on 2.1. Just when I thought AHKv2 had reached nirvana, and I wouldn't need to keep changing my codebase, your 2.1 is so compelling, that I know I my codebase must have it! I write in a lot of languages, and though my dev friends berate me for it, AHK is still my favorite.
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: [Wish] Try() Expression returning Unset

05 Sep 2023, 21:29

There is a big difference between Throw() and Try. Functions were already able to throw exceptions. Throw doesn't need to do anything special with its parameter; the parameter is just evaluated like normal and passed to the function.

Try, on the other hand, needs to affect error-handling of operators and function calls within the "parameter list". A function can't do this. It would have to effectively be a kind of operator, with any exception thrown by that part of the expression transferring control to the ??. There would be no point in it actually generating unset, because the transfer of control must be from the operator or call which threw, to the right-hand sub-expression of ??. Obviously, in a normal function call, the function won't be called if there is an error evaluating its parameters.
sirksel
Posts: 222
Joined: 12 Nov 2013, 23:48

Re: [Wish] Try() Expression returning Unset

09 Sep 2023, 18:04

Thanks @lexikos. I didn't appreciate that distinction. Well, my current attempt at a tryu (i.e. try-or-unset) to complement the functions above works perfectly, even with the changes you've already made:

Code: Select all

tryu(fnc) {             ;try or unset
  try return fnc()
  return unset
}
calc(x,y) => tryu(()=>x//y) ?? 0    ;some error-prone calc
MsgBox calc(6,3)        ;normal output 2
MsgBox calc(6,0)        ;div 0 error causes output 0
Very slick! The ?? works so well with returned unsets, and it will simplify so many expressions in my code that involve returning a sentinel, storing the returned value, and then returning with a ternary. I think I will also use unset as a sentinel for returns of not-found conditions, etc. and the caller can be in charge of the ?? defaultval.

P.S. These three changes (return unset, () {}, and structs) sucked me into the alpha, even after I'd vowed to keep things final for a while. A great problem to have! I'm spending my weekend making changes to my codebase. Thanks again.
sirksel
Posts: 222
Joined: 12 Nov 2013, 23:48

Re: [Wish] Try() Expression returning Unset

10 Sep 2023, 03:27

One more thought... The biggest advantage of tryu(fnc) to my tryd(fnc, dft) above is that tryu will never evaluate the default value (the right-hand side of ??) unless the try fails and tryu returns unset. The more I use it, the more I love this expanded implementation of unset. Thanks so much for that. I'll now convert a lot fewer short fat arrows to blocks, and even those that I do, I've got the new (){} syntax.

Btw, @lexikos, is there a chance you'd entertain a special symbol for static declarations to avoid that last remaining issue that routinely (at least, for me) disqualifies certain otherwise short fat arrows? Maybe f(x) => (@a := 0, a += x), where @a := 0 means static a := 0 but is allowed in an expression? If you'd consider it, I can write it up as its own topic.
Last edited by sirksel on 10 Sep 2023, 10:32, edited 1 time in total.
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: [Wish] Try() Expression returning Unset

10 Sep 2023, 03:42

I'm not likely to reserve any single character for static variables.

I might implement static.a as an extension of namespace resolution when I implement module support.

It's not as efficient, but you can do this (in v2.1-alpha.2+):

Code: Select all

f(x) => (f.a ??= 0, f.a += x)
MsgBox f(1)
MsgBox f(2)
If f is a closure, the property is of course added to the closure (i.e. each instance of the closure gets its own f.a).
lexikos
Posts: 9592
Joined: 30 Sep 2013, 04:07
Contact:

Re: [Wish] Try() Expression returning Unset

10 Sep 2023, 03:55

Of course, if f is a closure, you can instead put a in the outer scope and capture it.

Code: Select all

f := ((a) => (x) => a += x)(0)
MsgBox f(1)
MsgBox f(2)
You can also Bind a VarRef to a ByRef parameter.
sirksel
Posts: 222
Joined: 12 Nov 2013, 23:48

Re: [Wish] Try() Expression returning Unset

10 Sep 2023, 10:32

I totally understand, and thank you for the suggestions. I had never even thought about the first one, which is nice with the recent ??= change. The second option (closure) is what I've been mostly been using in this case (kind of like what I've been doing with the arity for custom enumerators). I might also just use (){} for some of the medium complexity ones, since that's also an option now, thanks to your work in these latest improvements. Lots of possibilities since you've given us so much flexibility.

Return to “AutoHotkey Development”

Who is online

Users browsing this forum: No registered users and 122 guests