Optional promotion and empty optional chains

Perhaps I did not describe the situation as clearly as I could have.

Swift has an existing feature called “optional promotion”. This means that, if a non-optional value appears in a location where an Optional is expected, that value is promoted to (aka. “wrapped in”) an optional.

This is the existing language behavior, and we can demonstrate it with a simple operator:

prefix operator ^-^

prefix func ^-^ (x: Int?) {
  switch x {
  case .some: print("Something")
  case .none: print("Nothing")

let n: Int = 7
^-^n      // "Something"

Here we take n, which a non-optional Int, and use it as the argument to an operator which expects an optional Int. This is only possible because n is automatically promoted from Int to Int?. We call this behavior “optional promotion”, and it exists because it is widely useful.

Thus, it is unexpected, surprising, and inconsistent with the rest of the language that optional chaining—which expects an Optional—does *not* utilize optional promotion. The compiler currently prohibits the use of a non-optional at the start of an optional chain.

Throughout the rest of the language, a non-optional can generally be used anywhere that an optional is expected, because it will be promoted to Optional as needed. But this is not the case for optional chaining.

I am suggesting that we make optional chaining more consistent with the rest of the language, by allowing optional promotion at the start of an optional chain, just as we allow optional promotion in other situations.

• • •

I hope that this detailed explanation helps to convey what I am trying to communicate. Namely, that the postfix question-mark would behave *more consistently* with the rest of the language (and with other operators that accept an optional) if we make this change.

Currently, optional chaining is *inconsistent*, because it prevents optional promotion. We could lift that restriction, and thus make optional chaining more consistent with the rest of the language in allowing optional promotion.

One natural consequence of doing so, is that an empty optional chain would have the practical and beneficial effect of allowing developers to write “x?” instead of “x as Optional”. The reason this works, is that optional promotion would apply in the same way as it does everywhere else.

Specifically, optional promotion would lift x to Optional before the optional chain is evaluated, because that is the only possible way in which the code makes sense. Then, the optional chain would be evaluated as normal. It takes an optional in, it performs the operations after the chain, and returns an optional.

When the optional chain is empty, that means it takes in an optional, performs no operations, and returns the result, which is trivially and obviously the same optional it started with. The optional chain operator—ie. the postfix question-mark—does exactly what it is supposed to.

It takes in an optional and returns an optional. That’s it. The fact that we can’t currently make it perform no operations in between, is an inconsistency. The fact that we can’t currently pass in a non-optional and let the compiler promote it to an optional, is an inconsistency.

The current behavior of optional chaining is inconsistent. I am saying we should fix those inconsistencies, and one practical benefit of doing so is that a drastically simpler way to spell optional values arises naturally from the standard behavior of optional promotion that exists in the language already.

1 Like