v2.1-alpha.2 implements an experimental directive,
#DefaultReturn unset.
sirksel wrote: ↑21 Nov 2022, 22:52
1.
?. such that
obj?.prop1[x]?.prop2(y1, y2)?.prop3? would return
unset if anything in the chain returned
unset or if any of those properties did not exist. Then one could write
obj?.prop1[x]?.prop2(y1, y2)?.prop3? ?? default, without all the separate tests and
HasProps. Maybe it's less of a new operator and more of an extension of the current
obj? "maybe" notation.
v2.1-alpha.2 implements this as an extension of the "maybe" operator. However, some notes:
If you write
obj?.prop1[x] ?? default, it will short-circuit if
obj is unset but for anything else it would throw UnsetError.
Only the part immediately preceding
? can be unset. For instance, if you write
.prop1[x]? or
.prop2(y1, y2)?, only the return value can be unset. prop1 and prop2 cannot be unset. JavaScript supports
.prop2?.(y1, y2), which would retrieve prop2 and then call it as a method only if it is not undefined. The parameters are not evaluated if the method is undefined. That implies the object must be able to report whether a method exists before it is called, which just means that it wouldn't support __Call, and mightn't support COM objects fully (if at all). For now,
.prop?.() is not permitted.
An ambiguity between !HasProp and prop returning unset arises only with
obj.prop?, which I think is acceptable. Just don't give the two conditions different meanings.
2. Perhaps some shortcut equality operator ?==, such that retval ?== compval is equivalent to IsSet(retval) ? retval == compval : unset and would preserve the short circuit.
I avoided setting the precedent of
comparing unset to a value, partly because I've been saying it's not a value. So, like most other operators, the equality operators and switch/case do not permit unset; e.g.
(a?) = b is a syntax error.
It occurred to me that the
? operator could short-circuit not only over member access, but almost anything. For instance,
(a + b? * c) ?? default would short-circuit
c,
* and
+ (which are evaluated in that order). Then instead of providing a default value to compare against (e.g.
(retval ?? {}) == compval always being false if
retval is unset) you would provide a default value for the result of the comparison, as in
(retval? == compval) ?? false, or you could put the short-circuit target (??) further outward if there are more conditions to be tested.
When
? short-circuits, it isn't permitted to be interpreted as an "omitted" parameter; there must be an additional
? if that is the intent.
I guess it could possibly even be more general... perhaps terminating a variable or expression with ? allows the unset to pass through any operator [...], causing the operator's result to be unset.
For optional chains, the
. operator doesn't have a result, because the operator isn't evaluated.
? short-circuits over the operator, leaving
unset as input for the next operation. In calculating the destination of the short-circuit at load-time, it is also able to determine whether the next operator can handle unset. If it can't, that's a syntax error (rather than leaving it to throw an error at runtime).
OpalMonkey wrote: ↑22 Nov 2022, 06:41
I keep finding myself trying to write
If param? rather than
If IsSet(param)
I chose not to permit this since interpretation as either
if IsSet(param) or
if IsSet(param) && param could be unexpected by some.
x? produces the value of
x if x is set, so the latter would be more correct.