nil in ARMA 3 SQF
ianbanks |
Most scripting languages have one single built in value that represents the
concept of "nothing", and they usually call it null. ARMA 3 SQF—not happy to
just be one of the crowd—has many different nil and null types, and has
special rules for evaluating code involving nil. This isn't a necessarily a failing of SQF—there are
good reasons for having both nil and null—but it does steepen the learning curve. So, if you've ever wondered why any, scalar or [0x2917924] show up, or why
you don't always get an error when scripts go bad, read on. When you access a variable that doesn't exist, a nil is returned: As well as returning a nil instance in an array, the above code will also cause an
Undefined variable in expression error if the script is running in a scheduled
environment. In an unscheduled environment, no error is returned. You can check this by running the
following in the (unscheduled) Debug console window: If you run the same code in a scheduled environment—created by using spawn—an error is displayed: In both cases, an undefined variable does not stop code from running, even
when the undefined variable error is shown. For example, the following code will
always result in a dead player: You can also create nil instances directly: Assigning a nil to a global variable using
the = statement is a special case, and removes the variable from the namespace.
Using setVariable with a nil value will also remove the variable from the namespace.
You can check this by using allVariables on the namespace: In contrast, assigning a nil to a private variable name does not remove the
variable. If a private variable is set to nil in an inner scope,
the inner variable continues to exist (with nil as the value) and the variables
in any outer scopes continue to be shadowed: The other way to verify that nil doesn't remove private variables is by
storing a specific type of nil—which is discussed below—and then printing it. nil instances are also treated as a special case when evaluating operators. If the
left or right-hand argument of an operator is a nil, the operator is not executed. Instead, the
result of the operator is simply just another nil:
If the operator has a side effect—such as hint, which has the side effect of displaying
a message on the screen—the side effect is also inhibited. When a nil is passed
to hint (or when you hint an expression that evaluates to nil), nothing happens at all:
Since SQF expressions are almost entirely operators, a nil appearing in an expression will
usually cause the value of the entire expression to be nil:
This is why errors involving nil often don't result in any error message. Accessing
an undefined variable in a non-scheduled environment—for example—won't raise an error. If the
undefined variable is then used in a larger expression, the expression will also evaluate to nil without
raising any errors. The nil evaluation rules cause a bit of an issue for isNil. The isNil operator, if
passed a nil on the right-hand-side, would not be executed and would instead evaluate to a nil: Instead, for global and local variables, the name of the variable must be passed in as a string: For everything else, the expression to be tested can be passed to isNil as a code block:
The code block is evaluated as if it was passed to the unary version of call—any
side effects are not suppressed. The isNull operator doesn't need to use strings or code blocks. The null
values—objNull and configNull for example—are passed into operators like
any other value. One of the few parts of SQF where nil has no special treatment is when it is used in an array: This is often used to print out nil values for debugging. It also reveals that—inside
the SQF engine—nil can have many different types. When the SQF engine evaluates a unary or binary operator with nil on the left or right hand
side—or on both sides—the operator is not actually executed. However, the arguments types
are still checked, and an error will be raised if the nil arguments do not have the required
types. A type error will also halt the evaluation of the script. For example, if the variable stringTypeNil contains a string type nil, the
following code will raise an error and won't run to completion: This code, however, works without error: If the types are correct on an operator expression involving nil, the result of the operator is a
nil with the return type of the operator. This can be abused to create nil values of a specific type: The nil returned from the nil operator, and the nil returned from an
undefined variable access, is a nil whose type is the wildcard any type. When placed inside
an array and converted to a string, it is either displayed as "nil", or in some cases, "<null>". A few examples of nil descriptive names are: Note that the numeric descriptions (such as 0x2917924 for objects) are
quite likely to be memory addresses in the ARMA 3 process, and they may change
between versions (or even runs) of ARMA 3.Variables and nil
[_variableThatDoesNotExist]; // Returns an array containing a single nil.
comment "No error is displayed, and no hint is shown.";
hint variableThatDoesNotExist;
comment "Displays an undefined variable error.";
[] spawn { hint variableThatDoesNotExist; }
hint variableThatDoesNotExist;
player setDamage 1;
hint [1, nil, 3];
someVariable = 1;
someVariable = nil;
hint str (allVariables missionNamespace find "someVariable");
_a = "brown";
call {
// Calling creates a new scope.
private "_a";
_a = "fox";
_a = nil;
hint _a; // _a is nil here, not "brown".
}
Evaluation with nil
1 + nil // Returns nil, but does not raise any errors.
hint _doesNotExist; // Has no effect, may raise an error.
hint nil; // Has no effect, never raises an error.
// Prints nothing, returns nil:
if (true) then { hint "Reached"; } else nil;
isNil
isNil _nilOrUndefinedVariable // Always returns nil, not true or false.
isNil nil // Also returns nil rather than true.
isNil (1 + nil) // Again, returns nil.
isNil "_nilOrUndefinedVariable" // Returns true if the variable is undefined or nil.
isNil { nil } // Returns true.
isNil { (1 + nil) } // Return true.
Displaying nil
hint str [nil]; // Hints "[any]"
hint str [player getVariable "undefinedVariable"]; // Hints "[<null>]", which is also a nil.
There are Many nils
someGlobal = [1 + stringTypeNil];
someGlobal = ["a" + stringTypeNil];
stringTypeNil = name nil;
numberTypeNil = abs nil;
unitTypeNil = leader nil;
Type
Printed
Example Code
ARRAY
array
getPos nil
BOOL
bool
local nil
CONFIG
0x2917a70
nil >> nil
SCALAR
scalar NaN
abs nil
STRING
string
str nil
GROUP
0x2917cac
group nil
OBJECT
0x2917924
leader nil