Skip to content

Using null conditional assignment

Vadim Dyachenko edited this page Jan 29, 2021 · 1 revision

Sometimes known as null coalescing assignment or logical nullish assignment, ??= is a shorthand "operator" for assigning something into a variable if it is currently set to undefined.

So,

a.b ??= c;

very literally translates to

if (a.b == undefined) a.b = c;

in the saved GML file. Should you use ++ or -- in the left-hand expression, you are your own enemy.

This is primarily useful for optional arguments in GMS2.3, like so:

function test(a, b, c) {
   b ??= "b";
   c ??= "c";
   show_debug_message([a, b, c]);
}
// ...
test(1); // [1, b, c]
test(1, 2); // [1, 2, c]
test(1, 2, 3); // [1, 2, 3]

Limitations

  • Left-hand expressions may not contain function calls.
    Or, rather, you can use them and GMEdit will save the code correctly, but will not collapse it back on load since I'm parsing the if-statement with a regular expression.

What about other null coalescing operators?

GML does not have inline assignments and inline blocks to allow for variously unusual manipulations that Babel does, therefore you cannot have these without at least one script call overhead. If that does not bother you, feel free to use #mfunc:

2.2 (in script ncset):

/// ncset(val)
/// @param val
globalvar ncval;
ncval = argument0;
return argument0 != undefined;

2.3:

globalvar ncval;
function ncset(val) {
	ncval = val;
	return val != undefined;
}

Macro function block (placed in the same script):

// equivalent of `a ?? b`
#mfunc ncand(one, two) (ncset(one) ? ncval : two)
// equivalent of `arr?[ind]`
#mfunc ncarr(arr, ind) (ncset(arr) ? ncval[ind] : undefined)
// equivalent of `obj?.field`
#mfunc ncdot(obj, field) (ncset(obj) ? ncval.field : undefined)

And then:

var z = undefined, a = 1;
trace(ncand(z, a)); // 1
trace(ncand(a, z)); // 1
trace(ncand(a, 2)); // 1

var arr = [1, 2, 3];
trace(ncarr(z, 1)); // undefined
trace(ncarr(arr, 1)); // 2

var st = { one: "one!" }
trace(ncdot(z, one)); // undefined
trace(ncdot(st, one)); // "one!"

Caveats

  • ncarr will reasonably crash when accessing indexes out of range.
    Adding these checks will cost you some performance.
  • ncdot will reasonably crash when accessing missing variables.
    Adding a check for that will cost you some performance.