There are no null pointers in Argentum

TLDR; in Argentum there is no separate nullablebool and optional types. It's the same concept, thus all operations, guaranties and safety measures are uniformly and seamlessly applicable all of them.

Nowadays, it has become fashionable to add Null safety to all programming languages. In Argentum, Null safety is achieved not by adding a new machinery, but by removing unnecessary one.

Argentum pointers are never nullable. If a nullable pointer is needed, an optional wrapper around the pointer is used, and optional-nothing does the same role as null-pointer in other languages:

// `a` is a non-nullable reference, initialized
// with a freshly constructed instance of `Point`
a = Point;

// `b` is an optional/nullable reference to `Point`,
// initialized with a `nothing` value.
b = ?Point;

b := a; // Now `b` reference the same location as `a`
b := Point; // Now `b` reference its own freshly created instance of `Point`
b := ?Point; // Now `b` is optional-nothing again.

v = b.x;     // Type error: `b` is not a pointer

// Ok, `b` is an `optional`, and as such a `bool` generalization.
// So no weird `??` `or_else` `?.` or other fancy grammar, just old good
// well-known former bool operations now generalized for all optionals..
v = b ? _.x : 0;

By the way, the syntax ?T for creating empty pointers signifies that in Argentum, all "null pointers" are strictly typed with their normal types and types are not erased when converted to and from "nulls".

In Argentum, the optional type is deeply integrated into the language. For different wrapped types, its internal representation varies. For example, optional pointers are actually stored as regular pointers, and optional-nothing is encoded as 0. This provides effortless marshaling to other languages through FFI, compactness of internal representation, and high operational speed.

From the language perspective, types Object and ?Object only differ at the compilation stage - the former doesn't require null-checks, while the latter, on the contrary, forbids access without checking. A similar approach is used for ?double, where optional nothing is simply NaN.

Since null pointers are mere optionals, and Argentum disallows to access inner value of any optional without prior checking for nothing-ness, Argentum programs cannot dereference null-pointers without null-checks. It's a syntax-driven safety.

On the other hand, once checked, value unwraps from optional and requires no more checking:

fn doSomething(maybeObject ?MyClass) {  // function explicitly declared as taking nullable
   maybeObject ? {                // A single check
      _.method();                 // From now on, no checking is needed
      log(_.field);
      _.field := expression;
      doSomethingDifferent(_);    // Call function, expecting non-nullable
   }
}

fn doSomethingDifferent(obj MyClass) {  // Non-nullable pointer
     obj.method();           // No checking needed
     doSomething(obj);       // Auto-conversion T -> ?T
}

In the last line when a MyClass value passed where a ?MyClass is expected, it wraps in optional automatically. Sometimes it's desirable to do this conversion explicitly:

// Variable `a` of type `?int` holding value 42
a = +42;

// Variable `c` of type `?Point` holding a newly created `Point` instance
c = +Point

Some might say Rust (Dart, Swift, Go) also has null-safety. Yes, but:

  • Using weird non-standard language constructs
  • Allowing to bypass null-safety or crash on nulls when you believe nothing happens

And since optional is a superset of bool, null checks are performed by the standard bool-related operators. Welcome back C/C++ style: if(ptr) use(ptr); this time in a safe and strictly typed manner: ptr ? use(_)

Leave a Reply

Your email address will not be published. Required fields are marked *