Subscript on Optional with default value

I think it would be nice for Optional to have a subscript for a default value instead of relying on the nil coalescing operator, as follows:

extension Optional {

    subscript(default defaultValue: @autoclosure () -> Wrapped) -> Wrapped {
        get {
            switch self {
            case .none: return defaultValue()
            case .some(let value): return value
            }
        }
        set { self = newValue }
    }

}

It can be used as follows:

let x: [Int?] = (0..<5).map { _ in Bool.random() ? 42 : nil }
print(x) // [Optional(42), nil, nil, Optional(42), Optional(42)]
print(x.map { $0[default: 0] }) // [42, 0, 0, 42, 42]

This behaviour matches Dictionary's subscript(_:default) functionality.

P.S.: This new subscript replicates Dictionary's existing default value subscript through currying, like so:

let dict = [1: "1", 2: "4", 3: "9", 4: "16"]
dict[-1][default: "0"] == dict[-1, default: "0"]

Looking forward to feedback on this. Thank you.

Edit: Made subscript settable.

1 Like

Why do you prefer this over the more concise existing version?

print(x.map{ $0 ?? 0 })
2 Likes

Suppose you have an object with a deeply nested optional variable:

struct Box {

    var value: Int?

}

struct Package {

    var box: Box

}

struct Container {

    var package: Package

}

var container = Container(package: Package(box: Box(value: 2)))

And you have to mutate the value of the deeply nested optional, providing a default value if it is nil, you could extract the value to a local variable, mutate it and set the optional with the result. Like so:

var value = container.package.box.value
value = (value ?? 4) * 2
container.package.box.value = value

This unnecessarily creates a local variable and this can be avoided as follows:

container.package.box.value = (container.package.box.value ?? 4) * 2

Notice how the code is being duplicated on either side of the assignment.

Providing a subscript with a default value makes the code more succint and elegant to read, as follows:

container.package.box.value[default: 4] *= 2

Avoiding the issue of code duplication was the motivation behind Bool's toggle() and Dictionary's subscript(_:default:) methods and I propose Optional get the same upgrade.

7 Likes

With a settable subscript this indeed looks like a nice addition. Personally I would like to see this change.

I'm not a fan of this, this makes Optional look like a Dictionary while it's something different entirely. The example you provided does make the code shorter but IMO it's not necessarily better in this case.

1 Like