Dart nullability syntax decision: a?[b] or a?.[b]

Kathy Walrath
Aug 26, 2019 · 4 min read

Update (3/2021): Thanks to your feedback, the Dart team decided to use the a?[b] syntax.

Dart is in the process of redesigning its type system so that individual types will be either nullable (expressions of that type can have the value null) or non-nullable. Later this year we’ll tell you more about the timeline and roll-out process, but eventually Dart code will be non-nullable by default (NNBD), and you’ll have to use special syntax to say that a type permits null values.

For example, to declare that an integer can be null, you’ll need to add a ? after the type:

int? someInt; // someInt can be null.

That question mark syntax might be familiar if you’ve seen Kotlin, Swift, or C# code. But some things are different — notably the subscript ([]) operator, which you most often see used for list or array access. C# and Swift use ?[]. The current plan for Dart (and, by the way, for ECMAScript) is to use ?.[] instead:

e1?.[e2] // null if e1 is null; otherwise it’s e1[e2]

This article takes you behind the scenes to explain the reasoning behind this decision, and to encourage you to weigh in with your thoughts and suggestions. Most of this content is based on a comment by Bob Nystrom (munificent) on language issue #376, which summarizes a discussion between Bob and the owner of the NNBD specification, Leaf Petersen (leafpetersen).

Why use a dot?

Both options for [] have advantages.

e1?[e2]:

  • Follows C# and Swift

e1?.[e2]:

  • Is similar to cascade syntax:
    e1..[e2] // cascade syntax
    e1?..[e2] // null-aware cascade syntax

We spent a while trying to come up with ways to avoid the ambiguity of ?[. The problem is that you can’t tell whether code like { e1 ? [e2] : e3 } is a set literal containing the result of a conditional expression, or a map literal containing the result of a null-aware subscript.

If we add parentheses to make that code unambiguous, we could either choose to add them around the entire expression — { (e1 ? [e2] : e3) } — making it unambiguously a set literal. Or we could add them around the first part — { (e1 ? [e2]) : e3 } — making it unambiguously a map literal. But in the absence of parentheses, the parser doesn’t know what to do.

There are various solutions to this ambiguity, but none of them seem very satisfying. One approach is to rely on whitespace to distinguish between the options. In this approach, you always treat e1 ? [e2] as the start of a conditional expression, because there is a space between the ? and the [. And you always treat e1?[e2] as a null-aware subscript because there’s no space between those two tokens. But relying on whitespace can really harm the user experience.

In theory, relying on whitespace isn’t a problem in formatted code. But many users write unformatted Dart code as an input to the formatter. And that input format would thus become more whitespace sensitive and brittle in this corner of the language. So far, those kind of corners are very rare in Dart, which is a nice feature. (One such corner is that — — a and --a are both valid but mean different things.)

Ignoring the ambiguity, there’s another reason for using the dot: if we add null-aware forms for other operators — e1?.+(e2), etc. — we’ll probably want to require the dot, in which case requiring it for subscript is consistent with that future.

Another addition we’ve discussed for NNBD is a null-aware call syntax. If we don’t require a dot there, it has the exact same ambiguity problem:

var wat = { e1?(e2):e3 }; // Map or set?

Whatever fix we come up with for the ?[, we’ll also have to apply to ?(.

Finally, consider this example of chaining the subscript:

someJson?[recipes]?[2]?[ingredients]?[pepper]

To our eyes, that doesn’t look very good. It scans less like a method chain and more like some combination of infix operators — a little like ??. Compare it to this code:

someJson?.[recipes]?.[2]?.[ingredients]?.[pepper]

Here, it’s more clearly a method chain. Communicating that visually is important too, because users need to quickly understand how much of an expression will get null-short-circuited.

Putting all of that together, it seems like we should use the ?.[ form for the following reasons:

  • It avoids ambiguity problems. (The lexer already treats ?. as a single “null-aware” token.)

What do you think?

We’re always interested in feedback and suggestions. The best way to give feedback on this syntax is to comment (or thumbs-up a comment) on language issue #376. While you’re at it, consider checking out all of the other cool language features we’re working on.

Here’s where you can find more information:

NNBD:

Other Dart language changes and features:

An abbreviated code snippet that includes `String?`, `List<String>?`, and `return list?.[index]`.
A function that accepts a null argument and can return null.

Dart

Dart is a client-optimized language for fast apps on any platform.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store