Nullable Expressions

All four Elements languages allow the use of Nullable Types in standard Arithmetic and Logical Expressions and will automatically propagate nullability to their result type. So if one element of an expression is nullable, that information will bubble up through the expression tree and the result will be nullable, too.

When using the Swift language, nullables need to be of the implicitly wrapped type (i.e.declared with !) to be used directly, or they can be unwrapped inline with ?.

Consider the following example:

var x: Int32 := 5;
var y: nullable Int32 := 10;
var z: nullable Int32;

var a := x + y + z;
Int32 x = 5;
Int32? y = 10;
Int32? z;

var a = x + y + z;
let x: Int32 = 5
let y: Int32! = 10
let z: Int32?

let a = x + y + z?;

Both y and z are of nullable type. That means that the subexpression x + y will be promoted to be an Int32 as well, it's value will be 15. Next, this value is then multiplied by z, which is not only nullable but in fact nil. Regardless of z’s value, the end result will be a nullable type again, and because z is nil, the end result, 15 + nil, is nil. So this code declares a as a nullable Int32 (solely based on the input types of the expression) and at runtime a will evaluate to nil.

Determining the Type of a Nullable Expression

To determine the type of an expression, the following rules are applied:

  1. The nullability characteristic of the operands is ignored, determining the result base type in the usual way.
  2. If any one of the operands is nullable, the result type will be nullable as well.

Consider the following example:

var x: Int32 := 5;
var y: nullable Single := 5.0;

var a := x + y; // 10, a will be a nullable Single

In step one, the base types of x and y (Int32 and Single) are considered to determine that the resulting expression will also be of type Single. Then, because one of the parameters (z) is nullable, the entire result is promoted to a nullable Single.

Equality of Nullable Types

The above applies to all operators with only a single important exception: equality comparisons (= and / <> / !=) always result in a not-nullable Boolean to preserve the established logic of comparing reference-based values. (Other comparison operators, i.e. <, > and , will produce a nullable boolean as result if one of the operands is nullable, according to the above rules.)

For Example:

var x: Int32 := 5;
var y: nullable Int32 := 5;
var z: nullable Int32;

var a := x = y; // true, a will be a regular Boolean
var b := x = z; // false, b will be a regular Boolean

Determining the Result of a Nullable Expression

When evaluating expressions at runtime, the result will be nil, if one or both of the operands is nil; otherwise the result is the determined just as it would be for non-nullable expressions by applying the operator(s) to the respective values. It is worth noting that a single nil in a complex or nested expression will "bubble up" and turn the entire expression nil.

Examples (assuming the right-hand operator is a nullable Int32 type):

var x: Int32 := 5;
var y: nullable Int32 := 10;
var z: nullable Int32;

var a := x + z; // = nil
var b := x + y; // = 15;
var b := (x + z) * y; // = nil;

Notes

  • The above rules are specific to nullable types, but do not necessarily apply to custom class based types that implement their own operators. For example, the + concatenation operator on Strings will preserve the original string when appending a nil string via +.
  • if, while and until statements will accept nullable booleans, and treat nil as false.

Result Tables

The following tables provide a matrix for how nil and Boolean values interact.

Equality

The following rules apply to the equality (= and /<>/!=) operators:

  • nil = nil => true
  • nil = non-nil => false
  • non-nil = nil => false
  • non-nil = non-nil => compare value

Non-Equality

  • nil ≠ nil => false
  • nil ≠ non-nil => true
  • non-nil ≠ nil => true
  • non-nil ≠ non-nil => actual values are compared

Booleans

Truth table for the not / ! boolean operator

  • (not true) => false
  • (not false) => true
  • (not nil) => nil

Truth table for the and / && boolean operator

  • true and true => true
  • true and false => false
  • true and nil => nil
  • false and true => false — via Boolean Short-Circuit
  • false and false => false — via Boolean Short-Circuit
  • false and nil => false — via Boolean Short-Circuit
  • nil and true => nil
  • nil and false => false
  • nil and nil => nil

Truth table for the or / || boolean operator

  • true or true => true — via Boolean Short-Circuit
  • true or false => true — via [Boolean Short-Circuit
  • true or nil => true — via [Boolean Short-Circuit
  • false or true => true
  • false or false => false
  • false or nil => nil
  • nil or true => true
  • nil or false => nil
  • nil or nil => nil

Truth table for the xor / ^ boolean operator

  • true xor true => false
  • true xor false => true
  • true xor nil => nil
  • false xor true => true
  • false xor false => false
  • false xor nil => nil
  • nil xor true => nil — via Boolean Short-Circuit
  • nil xor false => nil — via Boolean Short-Circuit
  • nil xor nil => nil — via Boolean Short-Circuit

Note: There is no XOR operator in SQL, which is where the nullable truth tables are based on, however, "A xor B" can be expressed as "not (A and B) and (A or B)" and the above truth table derived from that.

Truth table for the Oxygene implies boolean operator

  • true implies true => true
  • true implies false => false
  • true implies nil => nil
  • false implies true => true — via Boolean Short-Circuit
  • false implies false => true — via Boolean Short-Circuit
  • false implies nil => true — via Boolean Short-Circuit
  • nil implies true => nil
  • nil implies false => nil
  • nil implies nil => nil

Boolean Short-Circuit

Boolean Short-Circuit evaluation is possible for the following operators if the left operand has a specific value:

  • and/&&: false => false

  • or/||: true => true

  • xor/^: nil => nil

(Note that xor/^ does not ever short-circuit for non-nullable expressions.)