Generalizing keypath-to-function conversions

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?


IMO, the keypath syntax should work anywhere the equivalent closure syntax would. +1


Thanks @Joe_Groff! Do you think this should go through a full evolution cycle (for a small proposal), or could this behavior be considered a bug fix?

I'd like this bug(?) to be also fixed, when you generalize the conversion between keypath and function.

If we do make them fully general, will there be a performance impact? The root keypath performance bug is still open, though I can't reproduce the issue anymore (the real keypath and direct variants have the same timing in 5.5 for me), but later bugs like SR-11983 are still reproducible despite being marked as Resolved. If keypaths should be usable wherever closures are, it seems logic they would also optimize as well.


Key path traversal should be much faster than it currently is, but key path literals used as functions have no fundamental reason to be tied to key path traversal; they could just be turned into closures.

Terms of Service

Privacy Policy

Cookie Policy