Add non-nil alias to Luau syntax as a counterpart to "?"

Currently, the only way to denote a nilable value as non-nil in Luau is to cast the value to its non-nil variant. In some cases, there are guaranteed scenarios where a nilable value is known to be non-nil by the developer.

I propose adding some symbol, say !, that can be used to quickly tell the type checker that a value is not nil. Effectively, it is a negation to the ? operator: Where ? declares a value as potentially nil, ! can be used to declare that a nilable value is non-nil.

This change would improve code cleanliness, especially for cases where something’s return value is marked as nilable, but at the time it is called the developer knows it is non-nil. This problem becomes exponentially worse as nested types are encountered, or otherwise long chains of references.

The primary purpose of this change is to avoid using if statements, because as far as I’m aware, Luau casts are exclusively for the type checker right now, and so live code processing doesn’t take time figuring out a condition when running these lines, that is, it’s effectively identical to flat out ignoring nil value warnings. It’s also quite messy to have a chain of if statements for single-line operations that ! would benefit.

A simple example might be a reference to a Motor6D:

-- This...
local myJoint: Motor6D = ... -- Just for something usable.
-- Pretend Part0 is undeniably populated with a valid reference.
print((myJoint.Part0::BasePart).Name) -- Without the cast, this complains about
-- nullability of Part0.

-- Turns into...
print(myJoint.Part0!.Name) -- This functions identically, quietly casting
-- Part0 to its non-nullable counterpart.

Or more extremely, some nested types:

type MyType = {
  Other: MyOtherType?
}
type MyOtherType = {
   Thing: Instance?
}

local data: MyType = {
	Other = { Thing = workspace }
}

-- This complains about the value being potentially nil
print(data.Other.Thing.Name)

-- ...And can be silenced with
print(((data.Other::MyOtherType).Thing::Instance).Name)

-- ...Which is a mess. But using this feature request, it can become
print(data.Other!.Thing!.Name)
8 Likes

Using ! for this purpose would be confusing, as in many other programming languages ! is used for not.

print(true!) -- true??

It may also be misinterpreted as doing factorial.

print(5!) -- 5??

! is based on the standards set by C# 8.0 onward (where nullable reference types are possible). It is invalid syntax on new value/reference declarations and raises a syntax error. It is also invalid on value types, but this doesn’t translate well to Lua because value types can be set to nil under language constraints. Still, in Luau, defining local foo: number = nil will raise a warning, improving behavior and strictness of an operator like !.

2 Likes

TypeScript uses ! as both not and a type assertion operator. Doesn’t seem to cause much confusion there, don’t see why it would cause any in Lua, where there’s no existing ! operator.

5 Likes

We have looked into support of null-forgiving and safe navigation operators and decided to accept the ‘nil-forgiving postfix operator !’ into Luau.
Proposal and discussion can be viewed at https://github.com/Roblox/luau/blob/master/rfcs/syntax-nil-forgiving-operator.md and RFC: nil-forgiving operator by vegorov-rbx · Pull Request #48 · Roblox/luau · GitHub

Note that we do not have the time frame for the implementation of this proposal right now.
We will report progress on this and other proposals in future Luau Monthly Recap posts.

5 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.