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

Ricardo Parada rparada at mac.com
Wed Apr 5 18:38:12 CDT 2017


Thanks Brent. Was that real code?  :-)

If it was, I did not realize it was possible to do something like that in Swift. That seems very cool, attractive and powerful. It certainly would look much better with the sugary syntax. 

I can imagine building a collection of sort orderings like this for example. 

// Order by lastName asc, firstName asc
let orderings = Person.lastName.asc().then(.firstName.asc())

Don't know if it would be possible but I think it would be nice to be able to do it. The asc() method would create an ascending sort ordering and would establish Root as Person.  I wonder if it would be possible to have the then() know that Root is person so that its argument can be a key path relative to Person.  Or maybe this can be expressed differently if this is not the right way. 

Anyways, the sugary syntax looks very attractive IMHO. 

This is what my Java code looks like for something like that:

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

or this:

NSArray<EOSortOrdering> orderings = 
    new NSArray<>(
        Person.LAST_NAME.asc(),
        Person.FIRST_NAME.asc()
    );


> On Apr 1, 2017, at 10:24 PM, Brent Royal-Gordon <brent at architechies.com> wrote:
> 
> 
>>> On Apr 1, 2017, at 5:56 PM, Karl Wagner via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>> 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”)
>> }
> 
> 
> You guys aren't thinking big enough.
> 
> 	// This implementation is closure-based because it's too complex for an email even *with* 
> 	// generics system upgrades. Without type system upgrades, things get *really* complicated, 
> 	// though probably still tractable.
> 	typealias Predicate<Element> = (Element) -> Bool
> 	
> 	func == <Root, Value: Equatable> (lhs: KeyPath<Root, Value>, rhs: Value) -> Predicate<Root> {
> 		return { $0[keyPath: lhs] == rhs }
> 	}
> 	
> 	func < <Root, Value: Comparable> (lhs: KeyPath<Root, Value>, rhs: Value) -> Predicate<Root> {
> 		return { $0[keyPath: lhs] < rhs }
> 	}
> 
> 	func && <Root>(lhs: Predicate<Root>, rhs: Predicate<Root>) -> Predicate<Root> {
> 		return { lhs($0) && rhs($0) }
> 	}
> 
> 	extension KeyPath where Value: Collection {
> 		func contains(where predicate: Predicate<Value.Element>) -> Predicate<Value> {
> 			return { $0.contains(where: predicate) }
> 		}
> 	}
> 
> That gives you:
> 
> 	let isPuppyQualifier = #keyPath(Pet, .type) == .dog && #keyPath(Pet, .age) < 12
> 	let familyQualifier = #keyPath(Family, .pets).contains(where: isPuppyQualifier)
> 	let familiesWithPuppies = Family.fetch(editingContext, familyQualifier)
> 
> Or, in one line with a more sugary syntax:
> 
> 	let familiesWithPuppies = Family.fetch(editingContext, (.pets).contains(where: .type == .dog && .age < 12))
> 
> -- 
> Brent Royal-Gordon
> Architechies
> 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170405/62ce2442/attachment.html>


More information about the swift-evolution mailing list