[swift-evolution] Make distinction between ?? -> T and ?? -> T?

Pyry Jahkola pyry.jahkola at iki.fi
Mon Mar 7 18:47:57 CST 2016


> On 07 Mar 2016, at 23:29, Sébastien Blondiau via swift-evolution <swift-evolution at swift.org> wrote:
> 
> I think there should be two distinct operators:
> 
> var certainlyOptional = value ?? otherValue
> var certainlyNotOptional = value ?! notOptionalValue

Consider a parallel Swiftverse where the standard library only defined the following single overload of `??`:

infix operator ?? { associativity right precedence 131 }

func ?? <T>(x: T?, @autoclosure y: () -> T) -> T {
    if let x = x { return x } else { return y() }
}

With that defined, life went on and things worked just fine:

let some: Int? = 1
let none: Int? = nil
let one = some ?? 2         // = 1: Int
let two = none ?? 2         // = 2: Int
let ein = none ?? some ?? 3 // = 1: Int

(That last line, by the way, is equivalent to `none ?? (some ?? 3)` because of the right associativity.)

However, because of implicit optional wrapping, the following lines compiled as well:

let un   = some ?? none // = Optional(1): Int?
let deux = 2    ?? none // = Optional(2): Int?
let rien = none ?? some // = nil: Int?

The reason was the compiler would Optional-wrap the left-hand side of `??` over and over again, until the argument pair met the shape `(T?, T)` for a type `T`, which happened to be `Int?` in this case:

some ?? none  →  Optional(some)        ?? none
2    ?? none  →  Optional(Optional(2)) ?? none
none ?? some  →  Optional(none)        ?? some

Don't you think the last line was especially surprising? Just a couple of lines up, the definition of `ein` looked almost identical:

let ein  = none ?? some ?? 3 // = 1:   Int
let rien = none ?? some      // = nil: Int?

…yet with an entirely different result! Of course that's just because of associativity rules, but who remembered! So I guess in our world, the intention with the second overload `(T?, T?) -> T?` is beginner friendliness, and I can't argue against.

— Pyry

Epilogue: The parallel Swiftverse, however, went their own way and protected fellow swiftizens against indecision with:

enum Nope {}

@available(*, deprecated,
    message="Your unwrappingness gets nowhere less Optional. See?")
func ** <T>(x: T?, y: T?) -> Nope { fatalError("How embarrassing!") }

Who knows, maybe one day they went on and invented a way to turn that thing into a compiler error? Who knows.

The End.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160308/e3e1b9bc/attachment.html>


More information about the swift-evolution mailing list