[swift-evolution] Smart KeyPaths

Joe Groff jgroff at apple.com
Wed Mar 29 21:08:16 CDT 2017


> On Mar 29, 2017, at 7:00 PM, James Berry <jberry at rogueorbit.com> wrote:
> 
>> 
>> On Mar 29, 2017, at 5:37 PM, Joe Groff <jgroff at apple.com> wrote:
>> 
>> 
>>> On Mar 29, 2017, at 5:26 PM, Michael J LeHew Jr via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> 
>>>> On Mar 29, 2017, at 5:12 PM, James Berry <jberry at rogueorbit.com> wrote:
>>>> 
>>>>> Referencing Key Paths
>>>>> 
>>>>> Forming a KeyPath borrows from the same syntax added in Swift 3 to confirm the existence of a given key path, only now producing concrete values instead of Strings. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the appending methods on KeyPath.
>>>>> 
>>>>> There is no change or interaction with the #keyPath() syntax introduced in Swift 3. #keyPath(Person.bestFriend.name) will still produce a String, whereas #keyPath(Person, .bestFriend.name) will produce a KeyPath<Person, String>.
>>>> 
>>>> This distinction seems arbitrary and confusing. The user is supposed tor remember that the #keyPath(Person.bestFriend.name) form produces a string while the #keyPath(Person, .bestFriend.name) form produces a key path object? I don’t think we’re advancing here. What would be the effect if just the former was valid, and (always/now) produced a keypath object that was convertible to string? How bad would the breakage be?
>>> 
>>> The syntax subtleties here are unfortunate. 
>>> 
>>> An idea that we discussed was to be able to tell when a #keyPath wants to be considered as a string and either implicitly or having some affordance for doing so. Back then this was harder because we had #keyPaths that could not be represented as a string (an earlier draft had keyPaths that could compose with closures; which while powerful, weren't really key paths any more. That idea was removed from the proposal we shared as they are intrinsically opposed to being able to serializing/deserialize key paths).  
>>> 
>>> Given that we don't support those kinds of key paths, nor are we really considering adding them back thanks to our desire to support serializing key paths to file in the future, this is a very reasonable idea I think. 
>> 
>> One small problem with the Swift 3 key path syntax when generalized to allow arbitrary Swift types at the root, and to also allow inference of the root, is that [...] can be either a subscript or an Array type reference, so it wouldn't be clear whether #keyPath([a].foo) is the path `.foo` rooted on the type `[a]` or the path `[a].foo` rooted in the contextual root type. We could say that you have to use a different syntax for a contextual keypath that begins with a subscript, like `#keyPath(.self[a])` or `#keyPath(.[a])`, perhaps.
> 
> To me it seems an acceptable compromise to require a leading dot for the contextual case:
> 
> 	#keyPath(Person.bestFriend.name)
> 	#keyPath(.bestFriend.name)
> 	#keyPath(.[a])

Another problem with overloading the same syntax is that ObjC key path checking has a bunch of special case logic to mimic Cocoa's KVC behavior, so that key paths involving string NSDictionary keys, NSArray's implicit mapping behavior, or untyped keys accessed through `id` work as one would expect in ObjC. We would only want to do that checking for ObjC key paths, so we should probably keep them syntactically distinct.

-Joe


More information about the swift-evolution mailing list