null coalescing operator assignment (foo ??= bar)
First, thanks for adding the null coalescing operator! I know 8.2.1794 was a year and a half ago, but I haven't been keep closely up-to-date with new features and I only just discovered it today. 😄
It's not difficult to work around the absence of this feature. But it seems like an inconsistency or an oversight, since most other similar operators have an assignment version.
let foo = foo ? foo : 'bar' " what I used to do
let foo = foo ?? 'bar' " what I'm doing now
let foo ??= 'bar' " what I'd like to do
Running that last line on v8.2.4842 gives the following message:
E15: Invalid expression: "??= "bar""
I personally find falsy-coalescing-assignment both easier to read and less likely to contain typos than those other more repetitive versions.
Thanks!
Edited to add:
The implementation of ??= should be subtly different from other operator= assignments, which convert a op= b into a = a op b. Instead, a ??= b should translate to something more like a ?? (a = b). E.g. see the ruby, javascript, or C# documentation.
What other languages have a similar assignment operator?
C# has it: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-coalescing-operator
What other languages have a similar assignment operator?
OTTOMH, Dart, JavaScript, PowerShell and PHP.
Yeah, I think it's a recent(-ish... since 2019?) addition to Javascript, C#, and PHP. One important distinction: their ?? and ??= operators evaluate the rhs only when the lhs is null or undefined.
IMO, the docs should be updated to reflect this difference. Perhaps something like the following?
- This is also known as the "null coalescing operator" but that's too
- complicated, thus we just call it the falsy operator.
+ This is similar to the "null coalescing operator" found in Javascript or C# or
+ PHP, except that it uses falsiness instead of null or undefined, thus we call it
+ the falsy operator.
Behaving less like a null coalescing operator and more similarly to vim's falsy-operator:
- Python's
a or b. But Python doesn't have any sort ofor=assignment operator, as far as I know. 🙁 - Perl's
a || b. I'm not looking it up now, but I think Perl had||=when I first tinkered with it, circa ~1996-7. -
Ruby added
||=in 1999. (ruby is my primary language)
It's worth noting that ruby's notion of "falsy" is much more restrictive than vim's or javascript's: nil and false are falsy and everything else is truthy. Idiomatic ruby uses ||= ubiquitously for setting variable with defaults and for simple memoization of method results. The ||= operator isn't as useful when we need to distinguish between nil, false, and undefined, but most of the time it's very nice to use.
Before I knew about the ?? falsy operator, the top of my .vimrc contained:
if empty($XDG_CONFIG_HOME) | let $XDG_CONFIG_HOME=$HOME..'/.config' | endif
if empty($XDG_DATA_HOME) | let $XDG_DATA_HOME =$HOME..'/.local/share' | endif
After I learned about ??, I updated it to:
let $XDG_CONFIG_HOME = $XDG_CONFIG_HOME ?? $HOME.."/.config"
let $XDG_DATA_HOME = $XDG_DATA_HOME ?? $HOME.."/.local/share"
And with ??= I'd be able to use:
let $XDG_CONFIG_HOME ??= $HOME.."/.config"
let $XDG_DATA_HOME ??= $HOME.."/.local/share"
IMO that last one isn't just less typing; it's also easier to read/understand when quickly scanning through the code.. Like I said up top, it's not difficult to work around the absence of this feature. But it sure is nice to use when I have it.
Thanks for your consideration!
looks golang had no such thing.. :-)
// which by if/else maybe more explicit..
or if had to be, then maybe simply just ?= was better?
I was just reminded of this ticket and wanted to share a detail that I left out in the description. The implementation of ??= should be subtly different from other operator= assignments, which convert a op= b into a = a op b. Instead, a ??= b should translate to something more like a ?? (a = b).
To quote from ruby's assignment syntax documentation:
There are also
||=and&&=. The former makes an assignment if the value wasnilorfalsewhile the latter makes an assignment if the value was notnilorfalse.Here is an example:
a ||= 0 a &&= 1 p a # prints 1Note that these two operators behave more like
a || a = 0thana = a || 0.
And MDN's description of Javascript's nullish assignment operator makes the same point:
Logical nullish assignment short-circuits as well meaning that x ??= y is equivalent to:
x ?? (x = y);And not equivalent to the following which would always perform an assignment:
x = x ?? y;
And C# specifies the same in its feature proposal, with more details. e.g:
- If
A0exists andBis implicitly convertible toA0, andBis not dynamic, then the type ofa ??= bisA0.a ??= bis evaluated at runtime as:var tmp = a.GetValueOrDefault(); if (!a.HasValue) { tmp = b; a = tmp; } tmpExcept that a is only evaluated once.
- Otherwise, the type of
a ??= bisA.a ??= bis evaluated at runtime asa ?? (a = b), except thatais only evaluated once.