In the proposal, now in Swift 5.2, we know keypath expressions are converted to functions. But these functions don't override equivalent functions. However, I can normally pass a non-optional value where an optional value is expected.

Should Swift allow these kind of function overrides like normal functions?

struct S {
    let val: String
}
class C {
    var block: ((S) -> String?)?
}

let c = C()
c.block = { (s: S) -> String in
    return s.val
}
// Compiles perfectly fine
c.block = { (s: S) -> String? in
    return s.val
}

let f: (S) -> String = \S.val
let f2: (S) -> String? = \S.val // Error: Key path value type 'String' cannot be converted to contextual type 'String?'

Where I ran into this was trying to use keypaths in allSatisfy which expects a function that throws.

class C {
    var block: ((S) throws -> String)?
}
let c = C()
c.block = { (s: S) throws -> String in
    return s.val
}
c.block = { (s: S) -> String in
    return s.val
}
[true, true].allSatisfy({ $0 }) // Compiles fine
[true, true].allSatisfy(\.self) // Error: Cannot convert value of type 'WritableKeyPath<_, _>' to expected argument type '(Bool) throws -> Bool'

Edit:
I just realized the .allSatisfy(\.self) example doesn't work, but if I refer to a property it does work just fine. I think it's an error with WritableKeyPath.

2 Likes