[swift-evolution] Smart KeyPaths

Matthew Johnson matthew at anandabits.com
Wed Mar 22 11:25:09 CDT 2017

> On Mar 22, 2017, at 11:00 AM, Vladimir.S <svabox at gmail.com> wrote:
> On 22.03.2017 18:47, Matthew Johnson wrote:
>>> On Mar 22, 2017, at 10:36 AM, Vladimir.S via swift-evolution
>>> <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>> On 22.03.2017 17:37, Ricardo Parada wrote:
>>>>> On Mar 22, 2017, at 9:30 AM, Vladimir.S <svabox at gmail.com
>>>>> <mailto:svabox at gmail.com>> wrote:
>>>>> let path = @Bag.things[0].name
>>>>> bag at path
>>>>> bag at .things[0].name
>>>>> bag at Bag.things <mailto:bag at Bag.things>[0].name
>>>>> bag.things[0]@.name
>>>>> bag.things[0]@Thing.name
>>>> It sounds like the @ character is serving two different purposes which
>>>> confused me at first.
>>>> If I understood correctly, you are using it to get the key path but also
>>>> to apply the key path to the bag struct and get the corresponding value.
>>> Yes. And the initial proposal suggest the following syntax accordingly:
>>> let path = Bag.things[0].name
>>> bag[path]
>>> bag[.things[0].name]
>>> bag[Bag.things[0].name]
>>> bag.things[0][.name]
>>> bag.things[0][Thing.name]
>> # makes a lot more sense than @ as a sigil.  It follows from #selector and
>> #keyPath.  These are the most similar language features right now where the
>> compiler produces special values.  I think it’s also worth noticing that
>> values produced by #selector and #keyPath are /used/ in normal ways.  There
>> is no magic syntax for their use, just a typed value.  If we’re going to
>> make a change we should use # instead of `.` for accessing these special
>> values but we should stick with subscript for use.
> Could you clarify, what do you suggest? Something like this:
> let path = Bag#things[0]#name

I would only use one # at the start of the key path.  Dots could be used afterwords like this: `Bag#things[0].name`  I think it is important to use normal expression syntax after the key path is introduced.

> bag[#path]

No, you would just say `bag[path]`.  `path` is a normal value and the subscript taking a path is a normal subscript.

> bag[#things[0]#name]

Here we have a type context expecting a key path with a root of the type of `bag`.  There is no potential ambiguity involved in using the `.` here unless people extend the key path types with static members themselves.  I think it’s fair to say do that at your own risk.  So there is no need to use special synatx - dot shorthand would work just fine with no ambiguity problem.  You could imagine the compiler synthesizing static members on key path types like this:

extension PartialKeyPath where Root == BagType {
    static var things: KeyPath<Root, ThingsType>  // or WriteableKeyPath or ReferenceWritableKeyPath

You just say: `bag[.things[0]#name]` using the existing dot shorthand for static members that return a value matching the type they are declared on.

On the other hand, it would be more consistent to introduce # shorthand here and not have the imaginary / synthesized static members on the key path types.  I’m neutral, leaning towards using # shorthand for this.

> bag[Bag#things[0]#name]

As above, there is no need for a second `#`.  Once the expression has produced a key path all subsequent chained accesses will also produce a key path.


> bag.things[0][#name]

As above, here we have a type context in the subscript that expects a key path.  We could use the existing dot shorthand and compiler synthesized static properties on key path types or just introduce a # shorthand.  The latter is probably better for consistency.

> bag.things[0][Thing#name]

Sure, if you don’t like the shorthand.

> ,and so
> let ref = Bag#foo()


One interesting thing to note is that we could also get deep references to unbound methods.  This would effectively combine key paths with unbound method references:

let doSomething = Bag#things[0].doSomething()

used like this:

Using # for key paths also allows us to build on it in the future for collection operators:

Bag#things[#sum].value //  a key path that sums the value of all things in a bag

Reserving this potential is one important reason to only use # where you are introducing a special key path expression and not everywhere in the key path chain.

> ?
> In this case I feel like the following will be more clean syntax:
> let path = #Bag.things[0].name
> bag[#path]
> bag[#.things[0].name]
> bag[#Bag.things[0].name]
> bag.things[0][#.name]
> bag.things[0][#Thing.name]
> let ref = #Bag.foo()

This is kind of weird because Bag doesn’t have a static things property.  I think it’s better to start put the # in the middle.  That said, this would work as well.

> And why subscript is the only good candidate for use?

It is how Swift models parameterized value access.  Building on that model makes a ton of sense.  I can’t imagine a compelling argument for using key path values.

> Actually, for me personally, any solution will be good as soon as it contains some 'marker' which saying "hey, here key paths are used. be aware."
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170322/6dc3bbb4/attachment.html>

More information about the swift-evolution mailing list