Introduce AnonymousKeyPath

Hey everyone,

This is my first time taking a crack at suggesting a feature for Swift, I hope some people would find this a compelling idea for an addition to Swift.

Problem

In many challenges of API Designs, you want your consumer to provide a way to get/set a specific concrete type.

Today, a consumer can express this idea by a Key Path, a wonderful concept in Swift. My consumer can tell me:

  • This is how you can get a String from MyObject (KeyPath)
  • This is how you can mutate a String on MyObject (WritableKeyPath)

Commonly, though, you want to ask a consumer a different set of questions:

  • Give me a way to retrieve a String on an arbitrary object
  • Give me a way to mutate a String on an arbitrary object

While not caring what is the Root of that String, as long as you fulfil the concrete requirement.

Unfortunately, as of today there's no way to express a Key Path which isn't bound to a specific concrete Root.

Solution

I'd like to introduce a new type of KeyPathAnonymousKeyPath. This key path does not care specifically what is the Root of the Keypath, but only the type of Value constrained to it.

To shorten it, while a regular key path suggests:

KeyPath<A, B> = (A) -> B

An anonymous keypath suggests:

// e.g., give me a way to get a type B, I don't care what is the root
AnonymousKeyPath<B> = () -> B

Effect on source compatibility, ABI stability, and API resilience

I'm hoping this could be an entirely additive change, where you could provide a \Object.property key path to an AnonymousKeyPath which would be abstracted from the specific Root object.

Other options

Using a non-KeyPath to bridge the gap, e.g.

public struct AnonymousLens<T> {
    public let get: () -> T
    public let set: (T) -> Void

    public init(get: @escaping @autoclosure () -> T,
                set: @escaping (T) -> Void) {
        self.get = get
        self.set = set
    }
}

Thank you for taking the time to read this :)

1 Like

How do you expect this to work? Supposing Object.property has type T, the reason that it expresses "give me an Object and I'll give you a T" is because you can do object[keyPath: \Object.property] to pull out Object.property. I don't see how this would extend to AnonymousKeyPath as you've described it. E.g., if you have:

struct Object {
  var property: Int
}

func digOut(_ keyPath: AnonymousKeyPath<Int>) -> Int {
  // What goes here?
}

foo(AnonymousKeyPath(\Object.property))

How would I use an AnonymousKeyPath<Int>, to "dig out" an Int value from somewhere?

1 Like

I think you're right, this probably isn't an easily additive change.
This is probably much trickier than I initially thought. It's almost like a key path protocol, which isn't something Swift knows how to do today unfortunately.

Might be worth leaving this open and seeing if anyone more knowledgable than myself has a creative thought about this, even from the compiler level.

FWIW, the AnonymousLens struct you've written is quite similar to the Binding type from SwiftUI.

I'm not sure how this would be used? If you don't know the type of the root object, how could you possibly know how to get a String out of it? I mean you don't know any of its properties.

1 Like

Why not use a protocol for this?

Hey All, thanks for your great feedback.
I've had some time to think about this some more and you're all right that this can't be expressed via a concrete type (as of today, at least).

I think what I have in mind is something akin to a KeyPath protocol, where you could pass any Key Path who's Value matches the generic constraint, regardless of its Root. I believe there's no way to express this today, but correct me if I'm wrong.

I'll adjust the pitch above to reflect this a tad later.