Closed RblSb closed 2 years ago
I find this useful. Null check + default value is quite common operation. And null coalescing would play nicely with null safety feature because of the typing:
var s:Null<String> = getNullableString();
$type(s == null ? 'default value' : s); // Still `Null<String>`
$type(s ?? 'defaultValue'); // would yield `String`
Seeing Haxe more and more as very high-level language, it would be a nice thing to have.
But I favour the ?:
variant for 2 reasons:
?:
is very consistent with the ternary operator, making it quite logical.??
is typographically ugly, reminiscent of hastly written scripts (with ????!!!
debug traces all over the place), distracting programmer. Consider:
function main() {
var x : Null<Foo> = getFoo();
var o = { foo : x ?: Foo.empty() }
}
vs
function main() {
var x : Null<Foo> = getFoo();
var o = { foo : x ?? Foo.empty() }
}
The first variant jumps much less at your eyes IMO. More relaxing maybe.
Edit: note according to this source Kotlin has no ternary operator. Yet it still uses the ?:
operator for null coalescing. This despite the fact it has abundant usage of ?
and !
. This might be another reason to double-think the usage of ??
for this evolution.
Reading about ?:
on https://en.wikipedia.org/wiki/Elvis_operator
there is also the historical usage for the Elvis ?:
operator, like in C++ or PHP.
tl;dr it couldn't be useful in Haxe because in these languages the concept of evaluating to true is so much less restrictive.
The name "Elvis operator" refers to the fact that when its common notation, ?:, is viewed sideways, it resembles an emoticon of Elvis Presley with his quiff.[1]
A similar operator is the null coalescing operator, where the check for boolean truthiness is replaced with a check for non-null instead. This is usually written ??, and can be seen in languages like C# (or Typescript).
In short, the usage of ?:
in C++ and PHP is to repeat the preceding result (for instance var x = f() ?: g()
is equivalent to var x = f() ? f() : g()
, but without evaluating f()
a second time). In C++ for example a non-empty string, non-zero int, or a non-null object would evaluate to true making this very useful.
In Haxe, since nothing can evaluate to Bool
implicitely, there would be no such use; hence we could not use it at all, or use it for null coalescing. C# also has ??=
, which is overkill IMO. But if you think differently it can be an argument for ??
, since ?:=
looks like the skull of Elvis Presley.
C# also has
??=
, which is overkill IMO.
Well, if ??
becomes a binary operator, I don't see a reason to disallow it in OpAssignOp
for the ??=
combination. It shouldn't have the short-circuiting issues of ||=
and &&=
. Besides, I find it pretty common to need to assign a default value if something is null
.
Fair enough, but the point of null coalescing operator is to reduce the LOC and the number of intermediate variables; that is why I think it is not as useful.
Besides, p ??= Foo.empty();
is less clear than if (p == null) p = Foo.empty();
I think,
or than p = p ?: Foo.empty()
.
Let's see:
pWindow ??= Foo.empty();
if (pWindow == null) pWindow = Foo.empty();
pWindow = pWindow ?: Foo.empty();
pWindow = pWindow ?? Foo.empty();
But it's probably a matter of practice, and in the end maybe you are in the right.
I would like to have many things from Kotlin, but when wiki says
This is an unusual choice of symbol, given that ?: is typically used for the Elvis operator, not null coalescing, but it was inspired by Groovy (programming language) where null is considered false.
I would stick on C#/JS/etc option. Even if it looks more strange - it's pretty common and easier to type. Also don't think that ternary operator and null coalescing are that similar, so we shouldn't unify it to same syntax.
??=
seems interesting, but i'm not brave enough to request this feature for now, something like this.x = y ?? this.x
looks good enough.
C# also has ??=, which is overkill IMO
??=
is perfect for non-constant default argument values, e.g.function filter(fn:T->Bool, ?out:Array<T>):Array<T> { out ??= []; for (item in items) if (fn(item)) out.push(item); return out; }
allowing to both call the function as-is to allocate a one-off array and to call it with an existing array to concat items to the end of it.
a = a ?? b
introduces a bit of redundancy in comparison as we are reassigning a
(which can be more complex than a local variable) even if it was fine.
Another vote for "??" since that is what Swift uses.
Especially the null-safe traversal operator would make a lot of sense when working with json files.
return options?.advanced?.id ?? 0;
vs
return options == null ? 0 : (options.advanced == null ? 0 : (options.advanced.id == null ? 0 : options.advanced.id));
or
return options == null || options.advanced == null || options.advanced.id == null ? 0 : options.advanced.id;
This proposal has been accepted, see https://haxe.org/blog/evolution-meeting-2021/ for details.
Rendered version