Hi all,
Wanted to open an evolution discussion based on this post over in #swift-users.
The short version is that currently, the keypath-to-function (SE-0249) conversion operates very narrowly—it only succeeds if the root and value types are an exact match. Function-to-function conversions, OTOH, are comparatively more permissive. Functions are covariant in their return types and contravariant in their parameter types during conversion. This leads to some potentially surprising results:
let f1: (S) -> Int = \.x
let f2: (S) -> Int? = f1
let f3: (S) -> Int? = { $0.x }
let f4: (S) -> Int? = { kp in { root in root[keyPath: kp] } }(\S.x)
let f5: (S) -> Int? = \.x as (S) -> Int
let f6: (S) -> Int? = \.x // <------------------- Error!
I'm curious to hear everyone's input as to whether we should allow keypath literals to partake in the full generality of function-function conversions (so that f6 above would successfully compile). This would also allow constructions like this:
class Base {
var derived: Derived { Derived() }
}
class Derived: Base {}
let g1: (Derived) -> Base = \Base.derived // contravariant in param, covariant in result
// EDIT: This is conversion is already permitted.
let g2: (Base) async throws -> Derived = \.derived // (Base) -> Derived converts freely to being 'async throws'
Would allowing these sorts of construction make the use of keypath literal syntax too opaque, or is it more unusual that keypath literals can't be used in all the same places that the equivalent "hand-written" closure could?