[v2] p? as shorthand for p:=unset Topic is solved

Propose new features and changes
iseahound
Posts: 1434
Joined: 13 Aug 2016, 21:04
Contact:

[v2] p? as shorthand for p:=unset

Post by iseahound » 26 Sep 2021, 08:54

Since we already have p* as the wildcard / kleene star, it would be nice to have a p? shorthand for making variables unset, as in p:=unset.

Code: Select all

sum(a, b?, c?) {
   b := IsSet(b) ? b : 0
   c := IsSet(c) ? c : 0
   return a + b + c
}
This is a bit of a stretch, and I admit I am using p:="" from v1 just fine. Thanks for making v2 as amazing as it already is.

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

Re: [v2] p? as shorthand for p:=unset

Post by swagfag » 08 Oct 2021, 22:58

sure, why not
also copy even more things from the C# spec, a ?? b null coalesce would be nicer instead of IsSet() with the ternary

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

Re: [v2] p? as shorthand for p:=unset

Post by iseahound » 17 Oct 2021, 12:19

Actually I did some more thinking, and it might be worth implementing a? as a maybe type to solve the problem of passing single unset parameters. Currently only the star * is capable of passing multiple unset parameters.

Code: Select all

fn(a, b := unset, c*) {
   ; Do something
   anotherfn(a, b?, c[1]?)
}
anotherfn(a, b:=2, c:=3) => a + b + c
? wouldn't do anything, except hide its value from the type checker. (As in preventing the evaluation of the parameter, e.g. normal order/lazy eval.) As always, calling IsSet() would be necessary to safely interact with the value. And if IsSet isn't called, and the value is unset, then a warning is thrown as expected.

I like the ability to check if a value is set or unset, and I do not believe that a null/undefined type is necessary. The only problem with unset as it is now is that only a * can pass multiple unset parameters, and there is no non-hacky way of passing single unset parameters. It would also solve the problem of destructuring an array within the function body as shown above.

This seems minimally invasive compared to permitting a statement such as p:=unset in the function body, or creating a null type, and directly solves the only problem with unset, which is the ability to be directly passed to other functions.

User avatar
jNizM
Posts: 3183
Joined: 30 Sep 2013, 01:33
Contact:

Re: [v2] p? as shorthand for p:=unset

Post by jNizM » 04 Feb 2022, 08:51

The same what swagfag has requested: wish for Conditional expressions: boolean

Long example:

Code: Select all

Function(p := unset)
{
	if (IsSet(p))
		return p
	else
		return "unset"
}
Ternary operator:

Code: Select all

MsgBox IsSet(p) ? p : "unset"
Conditional expression: boolean

Code: Select all

MsgBox IsSet(p) ?? "unset"

If not, I can live with that too.
[AHK] v2.0.5 | [WIN] 11 Pro (Version 22H2) | [GitHub] Profile

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

Re: [v2] p? as shorthand for p:=unset  Topic is solved

Post by lexikos » 01 Jul 2022, 20:07

v2.0-beta.6 implements the following items that were directly requested in this topic:
  • p? as shorthand for p:=unset in parameter lists of function declarations.
  • p? within a function call to permit an unset variable to act as though the parameter was omitted.
  • a ?? b, approximately equivalent to isSet(a) ? a : b, including short-circuit evaluation and the limitation that a must be a variable.
As a result, the sum function in the first post now works. Putting aside the fact that the default value (0) could have been defined directly in the parameter list in this example, it can also be shortened to this:

Code: Select all

sum(a, b?, c?) => a + (b ?? 0) + (c ?? 0)

The suggestion of c[1]? is not feasible at present, but passing b? from fn to anotherfn works.

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

Re: [v2] p? as shorthand for p:=unset

Post by iseahound » 13 Aug 2023, 11:26

@lexikos In the case where there may be an out-of-bounds index, is throwing an index error the intended behavior? (on v2.1-alpha)

Code: Select all

#Requires AutoHotkey v2.1-
MsgBox fn(1, 2, 3, 4) ; Extra Parameter
MsgBox fn(1, 2, 3)
MsgBox fn(1, 2) ; Missing Parameter

fn(p*) => forward(p[1], p[2]?, p[3]?) ; Destructure Array
forward(a, b:=0, c:=0) =>  a + b + c

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

Re: [v2] p? as shorthand for p:=unset

Post by lexikos » 13 Aug 2023, 19:44

Yes.

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

Re: [v2] p? as shorthand for p:=unset

Post by iseahound » 15 Aug 2023, 15:06

Do you think the IndexError should be wrapped with the question mark operator? Destructuring arrays (variadic parameters) with ? would still encounter problems currently with a check needed for array length.

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

Re: [v2] p? as shorthand for p:=unset

Post by lexikos » 16 Aug 2023, 05:51

@iseahound
I can make an assumption about what "wrapped" is supposed to mean based on context and the task you mentioned, but instead I'm going to suggest that you reconsider the meaning of the words you've used and rephrase the question so that it actually describes the behaviour that you want. Perhaps if you do that, you might see why it doesn't behave that way.

Also consider what the relation is between ? and UnsetError.

Code: Select all

#Requires AutoHotkey v2.1-
class x {
    static y => this.z ; UnsetError!
}
MsgBox(x.y?) ; Not shown.
Also consider assignments in relation to unset.

Then ask.

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

Re: [v2] p? as shorthand for p:=unset

Post by iseahound » 16 Aug 2023, 10:40

Okay, I see what you're trying to say. However do you think there is value in allowing the ? to destructure arrays? Especially since maps (via __Item) and properties won't encounter any "out-of-bounds" error like IndexError.

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

Re: [v2] p? as shorthand for p:=unset

Post by lexikos » 16 Aug 2023, 20:04

@iseahound
If you rephrase the question as I suggested, or explain to me what you think I meant by each of my statements instead of merely saying that you get it, I might believe that you have seriously considered my points and understand them.

As for IndexError, what do you think was the point of "Also consider assignments in relation to unset."?

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

Re: [v2] p? as shorthand for p:=unset

Post by iseahound » 17 Aug 2023, 11:40

Your previous post to me said that within the error propagation chain, the question mark operator can only "wrap" the final returned value, which is either "unset" (which is raised as an UnsetError) or a real value. Because the IndexError arises earlier in the error propagation chain, as in when checking for the space required to store and hold the value, the IndexError will be thrown regardless of the postfix ? because it's not related to the final return value. Instead it can be caught via try {} catch IndexError. This was illustrated in your code snippet above, where the UnsetError (despite being the "last" statement) is thrown earlier in the error propagation chain, and therefore also cannot be held vis the ? operator.

I don't fully understand your statement of "consider assignments in relation to unset". However I will take a guess as to the meaning, which I suspect refers to: array[2] := (value?) where value evaluates to unset. Going down this path one of two outcomes can occur:
  1. In array := ['a', 'b'], array[2] := unset outputs ['a'] where the array length is 1.
  2. In array := ['a', 'b'], array[2] := unset outputs ['a', ] where the array length is 2.

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

Re: [v2] p? as shorthand for p:=unset

Post by lexikos » 17 Aug 2023, 17:26

Why are you using the word "wrap"? It is not like try-catch. It does not enclose anything.

"unset" is not "raised as an UnsetError".
By default, any attempt to use an unset variable, property, array element or return value immediately causes an UnsetError to be thrown.
A function doesn't know whether its return value is being used or how, so can't be the one to throw UnsetError. UnsetError is thrown by the expression evaluator only if unset is not permitted. A statement such as fn() or expression like (fn(), true) also permits unset, because any value would only be discarded.

The operator just literally does what it says:
Permits a variable (or in [v2.1-alpha.2+], a property, method or function call) to be unset.
It does not "catch UnsetError under specific conditions" or the like. If an error is thrown, it is propogated regardless of ? or the type of error. If ? worked by catching UnsetError, it would be very difficult to distinguish between an UnsetError intended for the operator and an UnsetError thrown by a recursive call, which could come from a different call on the same line. It would also be very slow.

As for assignments, suppose that x[1]? returns unset when x.Length=0. What will x[1] ??= 1 do? What would it do for a map, variable or property?

Post Reply

Return to “Wish List”