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

Ricardo Parada rparada at mac.com
Sat Apr 1 15:36:04 CDT 2017

> On Mar 30, 2017, at 12:25 PM, Douglas Gregor via swift-evolution <swift-evolution at 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 <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?

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: 

``` java

    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:

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

Or qualifiers to filter elements in an array:

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

Or a qualifier for a query:

``` java
    EOQualifier qualifier = Person.BIRTH_DATE.lessThan(thirtyFiveYearsAgo);
You can also build qualifiers that work across relationships, whether they are to-one or to-many:

``` java
    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.

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

More information about the swift-evolution mailing list