Yo, dawg

EDIT: the behaviour of ?? has been altered as of Swift beta 6. There is now a special case for both the LHS and RHS being of T?, that matches the ternary version. However, the below is still of interest for details of implementing generic functions that take optionals.

The nil coalescing operator (a ?? b) is described in the Swift docs as shorthand for the following code:

a != nil ? a! : b

That is, if a is nil you get b, otherwise you get the unwrapped a.

Ok great, that’s pretty intuitive and very useful. You might be surprised then if you try the following:

let i: Int? = nil
let j: Int? = 5

// i is nil, so this evalates
// to j i.e. {Some 5}
i != nil ? i! : j

// but this returns nil
i ?? j

Why is it returning nil? Is the analogy with the ?: operator a simplification of what’s actually happening? Let’s see, by implementing our own version of the ?? operator, using the exact same ternary logic. Here it is: 1

infix operator ~~ {
    associativity right
    precedence 110

func ~~<T>(a: T?, b: @autoclosure () -> T) -> T {
    return a != nil ? a! : b()

// nope, still nil
i ~~ j

Hum. What’s going on?

Well, to be fair, this isn’t playing by the rules for ??. The docs also state “The expression b must match the type that is stored inside a.” Instead, I’ve passed the same type for both a and b, so the results are undefined.

What is actually happening is that the compiler is fixing the type of T to be the type of the argument on the right-hand side. So T is an Int?. That means the left-hand side T? is an Int??, or to write it longhand, an Optional of an Optional Int. 2

Then, in the call, i is being “upgraded” from an optional to an optional optional. This upconversion is a feature that allows you to pass in plain values to functions that take optional arguments, and have them be automatically wrapped in an optional, rather than having to manually construct an optional to pass in yourself.

This is useful when defining a function with default arguments (arguments which are, ahem, optional). For example the dump command takes a second parameter name: that you can choose not to pass in, that will prefix your dumped object data if you do. An implementation could look like this:

func mydump<T>(x: T, name: String? = nil) {
    if let name = name {
        // if the caller supplied a name, use it
        print(name); print(": ")

// this won't prefix the dump with a name:

// you could call mydump like this:
mydump(a, name: Optional("My object"))

// but there's no need, you can just do this:
mydump(a, name: "My object")

So back to our operator example. With the left-hand side being upconverted, by the time we’re inside our ~~ function, i has become Optional(i). Even if i == nil, Optional(i) != nil. It contains a value (of an optional that doesn’t contain a value).

To simulate what ?? is doing outside of a function, we can do this:

let ii = Optional(i)

// this actually evaluates to nil
ii != nil ? ii! : j

That an optional that contains a nil optional is not equal to nil is pretty important. If it were equal to nil, then iterating over sequences would be a problem. In the following example, the last two entries wouldn’t be reached:

let a: [Int?] = [1, 2, nil, 3, 4]
// remember, for...in is short for the following:
var g = a.generate()
// while the result of next() isn't nil
while let i = g.next() {
    // i is now an optional, that will
    // contain nil on the 3rd iteration

Is this behaviour of ?? a bug? I dunno, probably not. You could prefer it to behave like the raw ternary operator, or fail to compile by somehow mandating the right-hand type really be what’s contained in the left-hand optional. But you could also say it’s behaving correctly, based on how the language works, and you might even need it to behave this way in some scenarios.

Either way, it’s a useful case study if you plan on implementing a generic function that takes optionals yourself.

  1. If you’re unclear on why the right-hand side is being declared with an autoclosure, read this post 
  2. I would have loved to put some angle brackets in there, but WordPress had other ideas. It certainly knows how to re-capitalize its name when I get that wrong, though! 

9 thoughts on “Yo, dawg

  1. I imagine adding an overload where the right hand side is of type () -> T? and b! Is used would make this work as intuition might expect, at the cost of a possible runtime error. The real issue is that a non-optional value is necessary in order to guarantee a value of type T can be the result.

    I wonder taking two type parameters

    • Accidentally posted too soon. I wonder if a second type parameter could force a type error for users who attempt to pass two values of the same Optional type:

      func ~~(a: T, b: @autoclosure () -> U) -> U {
      return a != nil ? a! : b()

      I’m on an iPad at the moment so I can’t try it, but it think it would work and avoid the confusion. Maybe Apple will fix ?? in the next release.

      In any case, I’m glad you called this out. It’s definitely something to watch out for as we design generic API.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s