nulls - CPULL/Dflat GitHub Wiki

Nulls

Dflat handles null safety explicitly through type modifiers, default behaviors, and dedicated language constructs.

Nullability Rules

  • Basic Value Types vs. Other Native Types: Basic value types (integers, reals, dec, bool, char, enums) are strictly non-nullable by default unless explicitly marked with a question mark (type?). Other native types (string, date, time, clock, lapse, addr), pointers, and all objects (classes, collections) are nullable by default; adding ? to them (e.g., date?, string?) is a compile error.
  • Strings: Strings are nullable by default, and a null string is treated interchangeably with an empty string ("").
i32 normalInt = 10;         // Non-nullable value type
i32? nullableInt = null;    // Explicitly nullable value type
string text = null;         // Strings are nullable by default
string emptyText = "";      // Treated interchangeably with a null string

Native Type Null Handling

  • date: Nullable by default; can be assigned null directly (date d = null), which sets it to the minimum possible value of an i64.
  • time: Nullable by default; a null time is represented by -1.
  • clock: Nullable by default; can be assigned null directly (clock c = null), where it evaluates to 0 (since Clock() never returns 0).
  • lapse: Nullable by default; null evaluates to code 000 (raw value 0).
  • addr: Nullable by default; can be assigned null directly (addr a = null), representing an unmapped or invalid memory location evaluating to 0.
date d = null;               // Valid: sets to minimum i64 value
time t = null;               // Valid: sets to -1
clock c = null;              // Valid: evaluates to raw value 0
addr a = null;               // Valid: evaluates to raw value 0

Propagation and Comparison

  • Arithmetic: Null values propagate automatically through arithmetic operations.
  • Equality & Sorting: Null values evaluate equal only to other nulls and sort first in ordering.
  • Reference Checks: Expressions like a == null may invoke overloaded equality operators, whereas a is null always performs a direct reference check.
i32? result = 10 + null;     // Evaluates to null (propagation)
bool check = (nullableInt == null); // May invoke overloaded equality
bool direct = nullableInt is null;  // Direct reference check

Unwrapping and Safety Mechanisms

  • enforce Construct: The enforce a { } else { } construct serves as the dedicated unwrap mechanism for nullable types (type?) and weak references (note that strings cannot be enforced).
  • Null-Safe Operators: Fully supports ?., ??, and ??=.
  • Member Access: Attempting to access members or properties on a null object reference immediately raises a NullPointerException. There is no force-unwrap operator (note that ! is reserved for copying, e.g., x!).

Null coalescing and assignment

i32 b = nullableInt ?? 0;    // Safely unwraps into non-nullable target
myClass ??= MyClassConstructor(); // Initialize the class if it was null

Null-safe navigation

string userName = user?.Profile?.Name ?? "John Doe";

user?.Profile?.Name evaluates safely if user/Profile is null and defaults to John Doe if any of the steps are null.

Enforce unwrap mechanism (for nullable types / weak references)

Foo? bar = GetNullableObject(); // ... maybe initialized maybe not ...
enforce bar {
    // 'bar' is safely unwrapped and non-nullable in this block
    bar.Baz();
} else {
    // Handle the null or error case
    print("There is no Foo");
}
⚠️ **GitHub.com Fallback** ⚠️