Make try? + optional chain flattening work together

Yup, I admit that was kind of a rhetorical question, because with the way generics work in Swift, this couldn't behave any differently unless try? did some run-time checking for whether the value is actually an Optional and nil.

And that's the consistency argument: With the proposed change, this code:

func doInBackground<T>(
    _ fn: @escaping () throws -> T,
    then: @escaping (T) -> Void,
    onError: @escaping () -> ()
) {
    DispatchQueue.global().async {
        guard let res = try? fn() else { onError(); return }
        then(res)
    }
}

will behave wildly different than the same code, just replacing T with a concrete type:

func doInBackground(
    _ fn: @escaping () throws -> Data?,
    then: @escaping (Data?) -> Void,
    onError: @escaping () -> ()
) {
    DispatchQueue.global().async {
        guard let res = try? fn() else { onError(); return }
        then(res)
    }
}

I'm not sure we have any other construct that changes behaviour this much depending on the types/when generics are "manually specialized" (does anyone have examples?), but I do know that in my opinion this would make try?'s behaviour inconsistent and hard to reason about, even ignoring the conflation of errors and result values.

4 Likes