Skip to content

Using null conditional operators

Vadim Dyachenko edited this page Dec 19, 2020 · 2 revisions

Null-conditional operators are syntactic sugar that allows easier access to potentially-amiss values.

So, for example, if you wanted to grab the nearest enemy's health or 0 if there isn't such an enemy, you would usually do something like

var nearest_enemy = instance_nearest(x, y, obj_enemy);
var nearest_enemy_health = nearest_enemy != noone ? nearest_enemy.cur_health : 0;

but with this you could do

var nearest_enemy_health = instance_nearest(x, y, obj_enemy)?.cur_health ?? 0;

With support for expressions, variable access, array access, and all the various GameMaker accessors, this can spare you a lot of typing at a minor cost.

How it works

Operator Syntax Equivalent to
?? a ?? b a != undefined ? a : b
?. a?.field (a != undefined ? a.field : undefined)
?[ a?[index] (a != undefined ? a[index] : undefined)
?[| list?[|index] (list != undefined ? list[|index] : undefined)
?[? map?[?key] (map != undefined ? map[?key] : undefined)
?[# grid?[#x, y] (grid != undefined ? grid[#x, y] : undefined)
?[$ struct?[$ field] (struct != undefined ? struct[$ field] : undefined)

Setting up

Since the left-hand expression may have arbitrary complexity and/or side effects, GMEdit uses a little trick - fn() ?? def will be compiled to nc_set(fn()) ? nc_val : def, where nc_set assigns nc_val and returns whether the argument is "valid".

So, you would start with making a script with a globalvar (or a macro if you are feeling pedantic) inside.

For GMS2.3, it could look like so:

globalvar nc_val; nc_val = undefined;
/// @param value
/// @returns {bool} Whether the value is non-null
function nc_set(v) {
	nc_val = v;
    return v != undefined && v != noone;
}

For older versions, the script would look as following:

/// nc_set(value)->bool
globalvar nc_val;
gml_pragma("global", "nc_val = undefined;");
nc_val = argument0;
return argument0 != undefined && argument0 != noone;

Then you would open your Project Properties (from menu or Ctrl+Shift+T), enter your script and variable names (nc_set and nc_val in this example) under "Syntax Extensions ➜ Null-conditional operators", and that's all! You can now use the syntax and check the transformed code in your saved file.

Notes

  • Although ?. and ?[ check that the context is not undefined, they do not check for whether a variable exists / whether the index is in-bounds, so you can still run into errors that way.
    For structs/instances, you can get around this by using the 2.3.1 ?[$key] accessor.
    Arrays can be replaced with lists depending on use case.
  • Since ?? does very literally convert into a ternary operator, you should be mindful of operator priority - so a + b ?? c + d would convert to a + b != undefined ? b : c + d (counting a towards condition), but a + (b ?? c) + d will be fine.
  • GameMaker does not allow ternary operator chaining so a ?? b ?? c will not compile but a ?? (b ?? c) or (a ?? b) ?? c are fine.

Further reading: