[Review] SE-0161: Smart KeyPaths: Better Key-Value Coding for Swift


(Douglas Gregor) #1

Hello Swift community,

The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for Swift" begins now and runs through April 5, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
Reply text
Other replies
<https://github.com/apple/swift-evolution/pulls#what-goes-into-a-review-1>What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

-Doug

Review Manager


(Karl) #2

What is your evaluation of the proposal?

+1

Is the problem being addressed significant enough to warrant a change to Swift?

Yes

Does this proposal fit well with the feel and direction of Swift?

Yes

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Lots of languages have key-paths, but usually they are strings. This adds some type-safety, so that’s nice.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick reading. I also have a couple of questions:

1) Can we get keypaths from protocol existentials?

protocol MyProto {
    var something: Int { get }
}
struct MyThing: MyProto { /* … */ }

let myProtoSomethingKeypath = #keypath(MyProto, .something)
let value: Int = MyThing()[keypath: myProtoSomethingKeypath] // seems reasonable, right?

2) What about if it has associated types?

let secondElement = #keypath(Collection, .subscript[1]) // if Collection.Iterator.Element had constraints, could we dig in to the instance?

let _: Int = [1,2,3][keypath: secondElement]
let _: String = [“a”, “b”, “c”][keypath: secondElement]

But I’d still +1 the proposal even if was limited to concrete types.

Also, that brings me to 2 related comments regarding the evolution of the feature:

1) It would be nice to allow keypaths of constrained protocol existentials

    let secondElementFirstCharacter = #keypath(Collection where Iterator.Element == String, .subscript[0].characters.first)

2) It would be nice to be able to get keypaths to members which we don’t know exist. Members and protocol conformances may be retroactively added by 3rd-party code.

And that’s all.

- Karl

···

On 30 Mar 2017, at 18:25, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for Swift" begins now and runs through April 5, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
Reply text
Other replies
<https://github.com/apple/swift-evolution/pulls#what-goes-into-a-review-1>What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

-Doug

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Rien) #3

  • What is your evaluation of the proposal?

+1

  • Is the problem being addressed significant enough to warrant a change to Swift?

I do not see this as a problem, but as added functionality that would be beneficial

  • Does this proposal fit well with the feel and direction of Swift?

100%

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Yes, but this is actually better due to type safety

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading.

One observation though: I feel that “PartialKeyPath” does not add information, “partial” leaves one swimming: "what exactly is partial?”.
“RootKeyPath” would be more expressive imo. If RootKeyPath would be unacceptable, I would still prefer “RelativeKeyPath” over “PartialKeyPath”.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

···

On 30 Mar 2017, at 18:25, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for Swift" begins now and runs through April 5, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
Reply text
Other replies
What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

  • What is your evaluation of the proposal?
  • Is the problem being addressed significant enough to warrant a change to Swift?
  • Does this proposal fit well with the feel and direction of Swift?
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md
Thank you,

-Doug

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Matthew Johnson) #4

What is your evaluation of the proposal?

+1. This is a fantastic proposal! I have been looking forward to the ability to access unbound properties ever since Swift was first released. I am very happy to see it coming to the language. The design of this feature is even better than I had been hoping for!

That said, I do feel like the syntax changes in the revised draft were a step backwards on two fronts. The new syntax is inconsistent with that of the syntax used for unbound methods. IMO all unbound members should be available using the same syntax. Second, the new syntax is (IMO) too cumbersome, especially in the shorthand case where a type context is known.

This feature can be leveraged to build libraries with DSL-like interfaces of various kinds. The more cumbersome syntax significantly reduces the elegance of these designs. This is unfortunate. As I have mentioned before, I think we should consider using the `#` separator to indicate the start of the unbound portion of a member chain. This aligns with the convention of # indicating compiler magic and is very lightweight.

While I am not fully satisfied with the current syntax I would not want to see that stop the proposal from being accepted. It is a huge step forward and new syntactic sugar could be added in the future without breaking old code.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes, this is a huge improvement!

Does this proposal fit well with the feel and direction of Swift?

Very much.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

It is better than other languages I have used where unbound property access was limited to direct properties, not entire chains or properties and subscripts.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

In depth study. And I am already writing code that will use Smart KeyPaths as soon as they are implemented.


(Ricardo Parada) #5

Hello Swift community,

The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for Swift" begins now and runs through April 5, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
What is your evaluation of the proposal?

+ 1

Is the problem being addressed significant enough to warrant a change to Swift?

It's a good start. Read below.

Does this proposal fit well with the feel and direction of Swift?

Yes.

I just wished we could come up with a more elegant notation than #keyPath(Root, path). But I understand that just about all the special characters suggested are taken or reserved, i.e. $, ::, #, @. I favored the original syntax before the review period. If you keep reading you'll understand why.

The only other alternative I can think of would be to rename the existing #keyPath() to #objcKeyPath() and make #keyPath() exclusive to swift key paths. But it's probably not much of an improvement over #keyPath(Root, path). In fact, if we wanted to get the key path of a static member then I think the proposed syntax would be better, i.e. #keyPath(Root.Type, staticMember)

My goal would be to be able to extend these smart keys some day and use them with a object-to-relational-mapping module in Swift. I would love to be able to construct orderings, qualifiers for my queries with code that looked like this:

let isPuppyQualifier = Pet.type.equals(.dog).and(Pet.age.lessThan(12))
let familiesQualifier = Family.pets.hasAtLeastOne(satisfying: isPuppyQualifier)
let familiesWithPuppies = Family.fetch(ec, familiesQualifier)

I believe that in order to be able to use them like this, they would have to be extensible and use this notation. They would also have to be converted to strings. The reason is that the string can be used in combination with an object model to figure out the columns to use when building SQL.

I don't know if Swift will evolve smart key paths in this direction, but it's an area that I think is begging for improvement compared to other languages that I use. I think Swift could make a difference. It would certainly attract a large number of developers working on the server side writing this type of code on a day to day basis.

Having a very elegant object-to-relational-mapping module is very important. I really hope that Apple enables it and someone whether Apple or a third party is able to create something as nice as WebObjects and the Enterprise Objects Framework.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I have used key paths in java / WebObjects. WebObjects is a really nice java-based library from Apple that originated in Objective-C during the NeXT era. It has key-value-coding and an Enterprise Objects Framework (which Craig Federighi helped design while at NeXT). It is similar to CoreData but it can connect to just about every database out there. Project Wonder has enhanced the experience and added source code generation from object models.

The code generated from the object models has keys and key paths that contain string information as well as some type information similar to these smart key paths being proposed by SE-0161. The java code for these keys looks similar to this:


    public static final KeyPath<String> FIRST_NAME = new KeyPath<String>("firstName");
    public static final KeyPath<String> LAST_NAME = new KeyPath<String>("lastName");
    public static final KeyPath<Date> BIRTH_DATE = new KeyPath<Date>("birthDate");

For example, the above declarations are at the top of _Person.java class which is automatically generated from an object model. Person then extends _Person to get all the code automatically generated which includes getters and setters, i.e. lastName() and setLastName(value).

I then use key path objects to build sort orderings:

    NSArray<EOSortOrdering> sortOrderings = Person.FIRST_NAME.asc().then(Person.LAST_NAME.asc());

Or qualifiers to filter elements in an array:

    personsOlderThan35 = persons.filtered(Person.BIRTH_DATE.lessThan(thirtyFiveYearsAgo));

Or a qualifier for a query:

    EOQualifier qualifier = Person.BIRTH_DATE.lessThan(thirtyFiveYearsAgo);

You can also build qualifiers that work across relationships, whether they are to-one or to-many:

	
    EOQualifier isPuppyQualifier = Pet.TYPE.equals(PetType.DOG).and(Pet.AGE.lessThan(12))

    // This qualifier could be used to fetch all the families that have puppies
    EOQualifier qualifier = Family.PETS.hasAtLeastOneObjectSatisfying(isPuppyQualifier);
    NSArray<Family> familiesWithPuppies = Family.fetch(editingContext, qualifier);

These key paths can be converted to strings and looked up in the object model. The object model has entities (EOEntity), attributes (EOAttribute), relationships (EORelationship), etc. They are roughly equivalent to NSEntityDescription, NSAttributeDescription and NSRelationshipDescription of CoreData.

From the keys in the key paths, the Enterprise Objects Framework (EOF) is able to look up in the entity the corresponding attributes of the same name and find the column name to which it is mapped in the database. This allows EOF to build the SQL.

I find WebObjects and Project Wonder solution very elegant, except for the ALL-CAPS notation used for the static constants that hold these keys with type information.

I wish CoreData (or similar object-to-relational-mapping module) in Swift eventually leverage smart keys and key paths to make our code beautiful when building sort orderings, qualifiers (predicates or whatever they are called in CoreData) to build queries or apply them in-memory to arrays. This is what I do every day.

For example, wouldn't it look much nicer if some day you could express the above like this in Swift:

let isPuppyQualifier = Pet.type.equals(.dog).and(Pet.age.lessThan(12))
let familyQualifier = Family.pets.hasAtLeastOne(satisfying: isPuppyQualifier)
let familiesWithPuppies = Family.fetch(editingContext, familyQualifier)

For those unfamiliar with EOF, the editingContext in the code above is an EOEditingContext which is analogous to NSManagedObjectContext.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

I've read just about every single reply in all threads related to Smart KeyPaths, before and after the review period.

···

On Mar 30, 2017, at 12:25 PM, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:


(Tino) #6

-1 (there has always somebody who disagrees :wink:

Yes, KeyPaths are nice, solve a problem that's probably significant, and type safety is very important in Swift… but I don't think it's that urgent to have this feature now:
Maybe there are some secret plans that depend on smart KeyPaths, but from my point of view, there are other things that should have higher priority.

Even if the Swift team had an empty task-list, I think it would be better not to rush here, and rather introduce key paths later as an aspect of a bigger picture:
Afaics, introspection and metaprogramming can't be considered fully designed, and I'd prefer key paths to be derived naturally from those topics, instead of pushing them now without knowing which direction the language will take.


(Brent Royal-Gordon) #7

Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

  • What is your evaluation of the proposal?

I continue to believe that the chosen syntax is unnecessarily cumbersome, that the design is overly conservative in terms of making sure people never "accidentally" use the feature, and that we could do a lot better.

But let's not throw the baby out with the bathwater. This is an *excellent* proposal which addresses a real pain point for Swift users in a way that's far better than not only the status quo, but any design Objective-C could have offered. The only problem is that the syntax is ugly, and we can always sugar it up later, once the community and core team are more comfortable with the feature.

(However, I will make one last-ditch syntax suggestion: `#keyPath<Family>(.pets.first)`, where the generic parameter list can be omitted when it can be inferred from context. I think juxtaposing `<>` and `()` is clearer than a comma followed by a dot.)

  • Is the problem being addressed significant enough to warrant a change to Swift?

How many "Swift isn't dynamic" blog posts have we all seen? This begins to address their concerns.

How many code generation solutions have people come up with for lenses? This lets us get rid of them *and* it's more efficient.

How many classes inherit from `NSObject` solely for introspection? This will let some of those convert to native Swift classes.

Yes, it's a very significant problem.

  • Does this proposal fit well with the feel and direction of Swift?

Yes; the lack of a property equivalent to unbound methods has always been a gaping hole in the language, the clever type design is particularly Swifty. I don't like the `#keyPath` part of the syntax, but the property/subscript chain with optional chaining is super-Swifty as well.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

In most respects, this is vastly superior to Objective-C KVC. I've never used another language's lenses in anger, but I'm familiar with the theory of them, and this proposal seems like a better solution than those too.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Does talking about it on the list a lot count?

···

--
Brent Royal-Gordon
Architechies


(James Berry) #8

On balance, I’m +1 on this proposal, with caveats below…

I’ve expressed, I don’t really like the #keyPath(Foo, .bar.baz) syntax, because I feel it is confusing to differentiate from the more natural #keyPath(Foo.bar.baz) legacy syntax. I really wish there were a good alternative, and would support renaming the legacy syntax to #objcKeyPath(Foo.bar.baz) and claiming #keyPath for swift key paths, or finding another more seamless way for the compiler to grok the usage intent such that the objc usage can be intuited and special cased.

James

Hello Swift community,

The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for Swift" begins now and runs through April 5, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution

What is your evaluation of the proposal?

Overall very positive, with cautions on the syntax.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes

Does this proposal fit well with the feel and direction of Swift?

Yes

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Java, objc.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Thorough.

···

On Apr 3, 2017, at 8:27 AM, James Berry <jberry@rogueorbit.com> wrote:

On Mar 30, 2017, at 9:25 AM, Douglas Gregor via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:


(Zachary Waldowski) #9

* What is your evaluation of the proposal?

Weak +0.5. This is absolutely needed, and I don't want to see the
feature broken down any further through interminable bikeshedding,
so I'd be more willing to let it ride as-is than miss out on having
this feature.

I was personally quite happy with the original proposal text. Worrying
about interaction with type properties reminds me of the pearl-clutching
when lightweight generics were added to ObjC over ambiguity with
protocol conformance statements, to which, IIRC, the involved people
stated, "Don't worry. The compiler's got this. It's its job."

I can't really get behind a syntax with this amount of clunkiness. The
second example — `foo[keyPath: #keyPath(.bar.baz)]` — is egregious.
Perhaps the proposal is right that you'd rarely see it, but I take
umbrage with "never". Teaching smart key paths will require building up
from base principles just like the proposal, and from that perspective
it looks like a syntax designed out of compatibility concerns in a much
older language, not the best it could be on its own.

The leading dot inference is extremely desirable to limit repetition,
but the difference in behavior with lookup for #selector and ObjC-style
#keyPath will be confusing for many. "Why was it not compiling?" "You
have a leading dot when you shouldn't." That doesn't sound like a
satisfying experience unless the diagnostic is truly superlative.

Ultimately, the Core Team and the community have to make an opinionated
decision about this feature's impact on the language. Though I
understand (and believe in) progressive disclosure, I don't think it
behooves us to "hide" "advanced" syntax. `#foo` sigils feel like compatibility-
driven hacks. If, like me, you think unbounded methods and unbounded
properties form the lowercase-f foundation for Swift's answer to Cocoa-
quality dynamism, then the feature should have that policy reflected
appropriately in its syntax.

* Is the problem being addressed significant enough to warrant a
   change to Swift?

Absolutely. See what I say above — the time is right for well-
considered, powerful, but low-impact language additions. Key-paths would
be a sea-change for data-flow (reactive, etc.) frameworks and for DSLs.

* Does this proposal fit well with the feel and direction of Swift?

The feature — yes. The syntax — not quite.

* If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?

I've extensively used Cocoa's similar features. I can see, perhaps
just barely, where AnyKeyPath would be NSKeyPath in another universe,
and in that sense it's a great accomplishment building out these
features for Swift.

* How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

In-depth study of the original proposal; followed the mailing list
threads; quick re-review of the modified proposal.

Sincerely,

  Zachary Waldowski

  zach@waldowski.me

···

On Thu, Mar 30, 2017, at 12:25 PM, Douglas Gregor wrote:


(Jordan Rose) #10

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md]

Hi, David, Michael, Joe. I'm sorry to admit I haven't been keeping up with all the discussion on this proposal, but I do want to ask a very important implementation question: how will key paths be represented? Specifically, I'm concerned about it possibly using reflection metadata, which contributes to both code size bloat and secrecy leaks. (Imagine the metadata for a property 'shouldConnectToDishwasherOSDevice" appearing in a new build of the iOS simulator.)

I can imagine a very simple but inefficient implementation strategy that's based around something like closures, and a more efficient one that would directly code-generate offsets for structs (and use symbolic offsets when doing cross-module generation). Both of these would sidestep the use of reflection metadata. Is that more what you were thinking?

(sorry for opening the Pandora's Box of "I should be able to introspect types in the libraries I use" again)

Jordan


(David Hart) #11

What is your evaluation of the proposal?

I am very excited about the usefulness of this proposal right now and about its potential in the future. This is big step towards enabling a lot of “dynamic” features.

But, like Brent, I continue to think that the syntax is much too heavy: I was a big fan of the original version of the proposal. If we take the code Brent wrote in this thread:

let isPuppyQualifier = #keyPath(Pet, .type) == .dog && #keyPath(Pet, .age) < 12
let familyQualifier = #keyPath(Family, .pets).contains(where: isPuppyQualifier)
let familiesWithPuppies = Family.fetch(editingContext, familyQualifier)

We can see that it’s much less readable than if written with the original proposal:

let isPuppyQualifier = Pet.type == .dog && Pet.age < 12
let familyQualifier = Family.pets.contains(where: isPuppyQualifier)
let familiesWithPuppies = Family.fetch(editingContext, familyQualifier)

Or with any other similar alternative:

let isPuppyQualifier = Pet::type == .dog && Pet::age < 12
let familyQualifier = Family::pets.contains(where: isPuppyQualifier)
let familiesWithPuppies = Family.fetch(editingContext, familyQualifier)

I would strongly suggest that the core team reconsider the syntax in the latest version of the proposal and revert to the original form or something similar. Even if the original syntax is chosen, the ambiguity issues that existed in that version is only a small price to pay next to the improvement in readability this popular feature will gain.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes, very much.

Does this proposal fit well with the feel and direction of Swift?

It does.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I have used key-paths in Objective-C, but this feature is more similar to lenses than to Objective-C’s key-paths. I have not used lenses in another language.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

An in depth-study.


(Joshua Alvarado) #12

Quesition about this proposal. Will it be possibly to create an array of
keyPaths and set an objects value with enumeration.

Example:
struct Foo {
    var firstName: String
    var lastName: String
}

let keyPaths = [#keyPath(.firstName), #keyPath(.lastName)]

for key in keyPaths {
    foo[keyPath: key] = "bar"
}

print(foo) // firstName = bar, lastName = bar

···

On Thu, Mar 30, 2017 at 10:25 AM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

Hello Swift community,

The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for Swift"
begins now and runs through April 5, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/
proposals/0161-key-paths.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager. When replying, please try to keep the proposal link at the
top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/
proposals/0161-key-paths.md

Reply text

Other replies

<https://github.com/apple/swift-evolution/pulls#what-goes-into-a-review-1>What
goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

   - What is your evaluation of the proposal?
   - Is the problem being addressed significant enough to warrant a
   change to Swift?
   - Does this proposal fit well with the feel and direction of Swift?
   - If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?
   - How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Doug

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Joshua Alvarado
alvaradojoshua0@gmail.com


(Tony Allevato) #13

Hello Swift community,

The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for Swift"
begins now and runs through April 5, 2017. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager. When replying, please try to keep the proposal link at the
top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

Reply text

Other replies

<https://github.com/apple/swift-evolution/pulls#what-goes-into-a-review-1>What
goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

   - What is your evaluation of the proposal?

+0.5. The concept is something the language very much needs, but please
reconsider reverting to the lightweight syntax of the original proposal—IMO
the version here is significantly worse in terms of legibility and
discourages its use by being noisy. The fact that keypaths have their own
nominal types that can be extended to introduce new behaviors makes it even
more critical that they be easy and clean to use. If this becomes they way
property behaviors are introduced into the language, the #keypath syntax
simply won't fly.

I too struggled with the lightweight syntax at first (unfortunately I
didn't have the time to weigh in on the original version of the proposal,
but I would have given it a +1 as originally written), but once I thought
about it more, I realized like many others that it lined up perfectly with
the syntax used for unbound method references. A "key path" is really just
an unbound property reference that also happens to be able to navigate more
deeply through the type graph. For that reason, it should use the same
syntax that unbound function references use.

I don't discount the argument that some have made that it's difficult to
distinguish between keypaths and static property expressions. If that's the
case, then the same problem *already exists* in Swift between unbound
method references and static property expressions. If that is deemed to be
a problem, we shouldn't fix it *only* for keypaths. Instead, keypaths
should be recognized as what they are—unbound property references—and use
the original syntax that aligned with unbound method references. *Then* we
should consider alternative syntaxes that would fix the problem for *both*
kinds of unbound references at once, in the same way.

   - Is the problem being addressed significant enough to warrant a
   change to Swift?

Yes. Being able to refer to properties in an abstract way fills a big hole

in the language.

   - Does this proposal fit well with the feel and direction of Swift?

Conceptually yes, but the #keypath syntax does not. It complicates what

should be an easy-to-use feature.

   - If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?

Just Objective-C, whose notion of keypaths differs from what is trying to

be done for Swift.

···

On Thu, Mar 30, 2017 at 9:25 AM Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

   - How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

Followed the original discussions and first review thread.

More information about the Swift evolution process is available at

https://github.com/apple/swift-evolution/blob/master/process.md

Thank you,

-Doug

Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #14

  • What is your evaluation of the proposal?

+1

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes

  • Does this proposal fit well with the feel and direction of Swift?

Yes

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Lots of languages have key-paths, but usually they are strings. This adds some type-safety, so that’s nice.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick reading. I also have a couple of questions:

1) Can we get keypaths from protocol existentials?

protocol MyProto {
    var something: Int { get }
}
struct MyThing: MyProto { /* … */ }

let myProtoSomethingKeypath = #keypath(MyProto, .something)
let value: Int = MyThing()[keypath: myProtoSomethingKeypath] // seems reasonable, right?

Yeah, that would be a natural thing to support.

2) What about if it has associated types?

let secondElement = #keypath(Collection, .subscript[1]) // if Collection.Iterator.Element had constraints, could we dig in to the instance?

let _: Int = [1,2,3][keypath: secondElement]
let _: String = [“a”, “b”, “c”][keypath: secondElement]

But I’d still +1 the proposal even if was limited to concrete types.

Also, that brings me to 2 related comments regarding the evolution of the feature:

1) It would be nice to allow keypaths of constrained protocol existentials

    let secondElementFirstCharacter = #keypath(Collection where Iterator.Element == String, .subscript[0].characters.first)

These would rely on supporting generalized existentials in the language at large.

2) It would be nice to be able to get keypaths to members which we don’t know exist. Members and protocol conformances may be retroactively added by 3rd-party code.

You could define a library function that applies a key path to a base value, if the base value is castable to the root type of the key path. That might be a useful enough thing to absorb into the standard library at some point, but it doesn't seem to me like it strictly needs to be part of the core proposal.

-Joe

···

On Mar 30, 2017, at 1:08 PM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:


(Dominik Pich) #15

What is your evaluation of the proposal?

+1

Is the problem being addressed significant enough to warrant a change to Swift?

Yes

Does this proposal fit well with the feel and direction of Swift?

Yes

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

None .. but I know I like type safety

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick reading.


(Pranshu Goyal) #16

- What is your evaluation of the proposal?

+1

   - Is the problem being addressed significant enough to warrant a change
   to Swift?

Yes

   - Does this proposal fit well with the feel and direction of Swift?

Yes

   - If you have used other languages or libraries with a similar feature,
   how do you feel that this proposal compares to those?

I do not have significant experience with this feature in other languages

   - How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

A quick reading.

···

On 31 March 2017 at 11:50, Rien via swift-evolution < swift-evolution@swift.org> wrote:

> • What is your evaluation of the proposal?

+1

> • Is the problem being addressed significant enough to warrant a
change to Swift?

I do not see this as a problem, but as added functionality that would be
beneficial

> • Does this proposal fit well with the feel and direction of Swift?

100%

> • If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?

Yes, but this is actually better due to type safety

> • How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?

Quick reading.

One observation though: I feel that “PartialKeyPath” does not add
information, “partial” leaves one swimming: "what exactly is partial?”.
“RootKeyPath” would be more expressive imo. If RootKeyPath would be
unacceptable, I would still prefer “RelativeKeyPath” over “PartialKeyPath”.

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Balancingrock
Project: http://swiftfire.nl

> On 30 Mar 2017, at 18:25, Douglas Gregor via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Hello Swift community,
>
> The review of SE-0161 "Smart KeyPaths: Better Key-Value Coding for
Swift" begins now and runs through April 5, 2017. The proposal is available
here:
>
> https://github.com/apple/swift-evolution/blob/master/
proposals/0161-key-paths.md
> Reviews are an important part of the Swift evolution process. All
reviews should be sent to the swift-evolution mailing list at
>
> https://lists.swift.org/mailman/listinfo/swift-evolution
> or, if you would like to keep your feedback private, directly to the
review manager. When replying, please try to keep the proposal link at the
top of the message:
>
> Proposal link:
>
> https://github.com/apple/swift-evolution/blob/master/
proposals/0161-key-paths.md
> Reply text
> Other replies
> What goes into a review?
>
> The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:
>
> • What is your evaluation of the proposal?
> • Is the problem being addressed significant enough to warrant a
change to Swift?
> • Does this proposal fit well with the feel and direction of Swift?
> • If you have used other languages or libraries with a similar
feature, how do you feel that this proposal compares to those?
> • How much effort did you put into your review? A glance, a quick
reading, or an in-depth study?
> More information about the Swift evolution process is available at
>
> https://github.com/apple/swift-evolution/blob/master/process.md
> Thank you,
>
> -Doug
>
> Review Manager
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
*Pranshu Goyal*
*iOS Developer*
*tlkn*


(Karl) #17

I wish CoreData (or similar object-to-relational-mapping module) in Swift eventually leverage smart keys and key paths to make our code beautiful when building sort orderings, qualifiers (predicates or whatever they are called in CoreData) to build queries or apply them in-memory to arrays. This is what I do every day.

For example, wouldn't it look much nicer if some day you could express the above like this in Swift:

let isPuppyQualifier = Pet.type.equals(.dog).and(Pet.age.lessThan(12))
let familyQualifier = Family.pets.hasAtLeastOne(satisfying: isPuppyQualifier)
let familiesWithPuppies = Family.fetch(editingContext, familyQualifier)

For those unfamiliar with EOF, the editingContext in the code above is an EOEditingContext which is analogous to NSManagedObjectContext.

Theoretically, you could do something like that with this proposal...

struct UnaryPredicate<Parameter, Result> {
    let evaluate: (Parameter) -> Result
}
struct BinaryPredicate<Left, Right, Result> {
    let evaluate: (Left, Right) -> Result
}

extension KeyPath where Value: Equatable {
    func equals(_ value: Value) -> UnaryPredicate<Root, Bool> {
        return UnaryPredicate { $0[keyPath: self] == value }
    }
    func equals<KP: KeyPath>(_ other: KP) -> BinaryPredicate<Root, KP.Root, Bool> where KP.Value == Value {
        return BinaryPredicate { $0[keyPath: self] == $1[keyPath: other] }
    }
}

let isDog = #keypath(Pet, .type).equals(.dog) // UnaryPredicate<Pet, Bool>
if isDog.evaluate(somePet) {
    print(“It’s a dog”)
}

let areSameLength = #keypath(Array<Int>, .count).equals(#keypath(Array<String>, .count))
// BinaryPredicate<Array<Int>, Array<String>, Bool>
if areSameLength.evaluate([1,2,3], [“a”, “b”, “c”]) {
    print(“same lengths”)
}

but the syntax isn’t as nice when you start chaining key paths together because #keypath() is a little loud. No doubt there will be plenty of libraries for this sort of thing, though.

- Karl


(Ricardo Parada) #18

By the way, does anybody know why the proposed syntax require a leading period for the key path?

#keyPath(Family, .pets.first)

In other words, why not just this:

#keyPath(Family, pets.first)

Or Brent's proposed:

#keyPath<Family>(pets.first)

···

On Apr 3, 2017, at 2:08 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

Proposal link: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

  • What is your evaluation of the proposal?

I continue to believe that the chosen syntax is unnecessarily cumbersome, that the design is overly conservative in terms of making sure people never "accidentally" use the feature, and that we could do a lot better.

But let's not throw the baby out with the bathwater. This is an *excellent* proposal which addresses a real pain point for Swift users in a way that's far better than not only the status quo, but any design Objective-C could have offered. The only problem is that the syntax is ugly, and we can always sugar it up later, once the community and core team are more comfortable with the feature.

(However, I will make one last-ditch syntax suggestion: `#keyPath<Family>(.pets.first)`, where the generic parameter list can be omitted when it can be inferred from context. I think juxtaposing `<>` and `()` is clearer than a comma followed by a dot.)

  • Is the problem being addressed significant enough to warrant a change to Swift?

How many "Swift isn't dynamic" blog posts have we all seen? This begins to address their concerns.

How many code generation solutions have people come up with for lenses? This lets us get rid of them *and* it's more efficient.

How many classes inherit from `NSObject` solely for introspection? This will let some of those convert to native Swift classes.

Yes, it's a very significant problem.

  • Does this proposal fit well with the feel and direction of Swift?

Yes; the lack of a property equivalent to unbound methods has always been a gaping hole in the language, the clever type design is particularly Swifty. I don't like the `#keyPath` part of the syntax, but the property/subscript chain with optional chaining is super-Swifty as well.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

In most respects, this is vastly superior to Objective-C KVC. I've never used another language's lenses in anger, but I'm familiar with the theory of them, and this proposal seems like a better solution than those too.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Does talking about it on the list a lot count?

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #19

I was planning to use direct offsets when they're knowable, and fall back to reflection when genericity or resilience gets in the way. In the case when we're accessing a property resiliently, we'd need to use accessors as a fallback anyway, so if the reflection metadata isn't there, you'd end up with a computed property access.

-Joe

···

On Apr 3, 2017, at 11:55 AM, Jordan Rose <jordan_rose@apple.com> wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md]

Hi, David, Michael, Joe. I'm sorry to admit I haven't been keeping up with all the discussion on this proposal, but I do want to ask a very important implementation question: how will key paths be represented? Specifically, I'm concerned about it possibly using reflection metadata, which contributes to both code size bloat and secrecy leaks. (Imagine the metadata for a property 'shouldConnectToDishwasherOSDevice" appearing in a new build of the iOS simulator.)

I can imagine a very simple but inefficient implementation strategy that's based around something like closures, and a more efficient one that would directly code-generate offsets for structs (and use symbolic offsets when doing cross-module generation). Both of these would sidestep the use of reflection metadata. Is that more what you were thinking?


(Joe Groff) #20

If you happen to have an already-initialized value `foo`, yes, but you wouldn't be able to initialize a value this way. That would require knowing that some assigning to some set of key paths will fully initialize a value. That's something we'd like to support in some way eventually, but not as part of this first proposal.

-Joe

···

On Apr 3, 2017, at 1:47 PM, Joshua Alvarado via swift-evolution <swift-evolution@swift.org> wrote:

Quesition about this proposal. Will it be possibly to create an array of keyPaths and set an objects value with enumeration.

Example:
struct Foo {
    var firstName: String
    var lastName: String
}

let keyPaths = [#keyPath(.firstName), #keyPath(.lastName)]

for key in keyPaths {
    foo[keyPath: key] = "bar"
}

print(foo) // firstName = bar, lastName = bar