<html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head><body dir="auto"><div><span></span></div><div><div><span></span></div><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div><span></span></div><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div><span></span></div><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div><span></span></div><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div><span></span></div><div><div><span></span></div><div><div></div><div><div><span style="background-color: rgba(255, 255, 255, 0);">I concur with Logan’s idea here on the general points, but let me add a bit more.&nbsp;</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><b><u>Here are some KeyPathy things I’d like to see in a future Swift:</u></b></span></div></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><div><span style="background-color: rgba(255, 255, 255, 0);">/// A set of PartialKeyPath&lt;Type&gt; guaranteed as:</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">/// (a) the entire set of keypaths for a type; and</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">/// (b) accessible given the current scope</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">Type.allKeyPaths()&nbsp;throws -&gt;&nbsp;[PartialKeyPath&lt;Type&gt;]</span></div></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">/// A set of PartialKeyPath&lt;Type&gt;</span><span style="background-color: rgba(255, 255, 255, 0);">&nbsp;guaranteed as:</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">/// (a) sufficient for initialization of an instance; and&nbsp;</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">/// (b) accessible given the current scope</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">Type.sufficientPartialKeyPaths() throws -&gt;&nbsp;[PartialKeyPath&lt;Type&gt;]</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">class Property&lt;KeyPath&lt;RootType,ValueType&gt;&gt; { &nbsp;&nbsp;</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">&nbsp; &nbsp; let keyPath: KeyPath&lt;RootType,&nbsp;ValueType&gt;</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">&nbsp; &nbsp; let value:&nbsp;ValueType</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">}</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">Type.init(with properties: [PartialProperty&lt;Type&gt;])&nbsp;</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">Type.init(copy: Type, overwriting properties: [PartialProperty&lt;Type&gt;])</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><div>The idea is a type<span style="background-color: rgba(255, 255, 255, 0);">&nbsp;can provide you a set of PartialKeyPath&lt;Type&gt; that is guaranteed</span><span style="background-color: rgba(255, 255, 255, 0);">&nbsp;as sufficient for initialization of an instance of the type, as long as the</span><span style="background-color: rgba(255, 255, 255, 0);">&nbsp;current scope lets you access it.&nbsp;</span></div></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><b><u>What would also be nice:</u></b></span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><div><div><span style="background-color: rgba(255, 255, 255, 0);">/// A set of PartialKeyPath&lt;Type&gt; guaranteed as</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">/// (a) the entire set of writable keypaths of Type; and</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">/// (b) accessible given the current scope</span></div><div><span style="background-color: rgba(255, 255, 255, 0);">AllWritableKeyPaths&lt;Type, Element&gt;</span></div></div></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div>(etc.) :D</div><div><br></div><div><span style="background-color: rgba(255, 255, 255, 0);">Note: in Swift 3.2/4, (of course), AnyKeyPaths and PartialKeyPaths&lt;T&gt; can already be downcast to more specific types like KeyPath&lt;T, E&gt;, WritableKeyPath&lt;T, E&gt;, etc., but only if you already know what T and E are at compile time (i.e. they are not generic).&nbsp;</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">I have found some bugs though; iterating through arrays of AnyKeyPath using “where” statements to limit the types is a buggy and unpredictable affair (I believe “filter(into:)” works best).&nbsp;</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div><span style="background-color: rgba(255, 255, 255, 0);">E.g.:</span></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div>extension Array where Element == AnyKeyPath {</div><div>&nbsp; &nbsp; func partialKeyPaths&lt;T&gt;() -&gt; [T] {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; return self.filter(into: [PartialKeyPath&lt;T&gt;]())&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; { result, keyPath in</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if let k = keyPath as? PartialKeyPath&lt;T&gt; {</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; result.append(k)</div><div>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; &nbsp; &nbsp; }</div><div>&nbsp; &nbsp; }</div><div>}&nbsp;</div><div><br></div><div><b><u>To what end?</u></b></div><div><span style="background-color: rgba(255, 255, 255, 0);"><br></span></div><div>What we sorely lack in Swift is a way to (failably) init an object from a set of keypaths and values without tons of boilerplate and/or resorting to using string keys etc.&nbsp;</div><div><br></div><div>Worse, right now there is no way to make a copy of an object/struct while mutating it only at one or two keypaths without writing yet more boilerplatey init methods.</div><div><br></div><div>Heck, right now, keypaths can be used for initializing <span style="background-color: rgba(255, 255, 255, 0);">neither&nbsp;</span>immutable instance constants, nor mutable instance variables that lack default initializers. E.g.: self[keyPath: \Foo.bar] = “baz” fails to compile inside an init method, because the property is not initialized yet. Gee.&nbsp;</div><div><br></div><div><b><u>Towards Type-Safe Instance Composition Patterns:</u></b></div><div><br></div><div>In a type-safe language we can’t have ECMA6-style destructuring (<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment">https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment</a>)... and please don’t accuse me of wanting it.</div><div><br></div><div>All I want is some sugar that makes the Swift compiler infer more different kinds of convenience init methods. Something like:</div><div><br></div><div>struct Foo {</div><div>&nbsp; &nbsp; let bar: String</div><div>&nbsp; &nbsp; let baz: String</div><div>&nbsp; &nbsp; let leadScientist: QuantumPhysicist</div><div>&nbsp; &nbsp; let&nbsp;<span style="background-color: rgba(255, 255, 255, 0);">labTech</span>: TeleporterTester</div><div>}</div><div><br></div><div>let fooMarch = Foo(bar: “asdf”, baz: “qwer”, leadScientist: QuantumPhysicist(“Alice”), labTech: TeleporterTester(“Bob”))</div><div><br></div><div>let fooApril = Foo(copy: fooMarch, overwriting: Property(\.labTech,&nbsp;<span style="background-color: rgba(255, 255, 255, 0);">TeleporterTester(“Charlie”)</span>)&nbsp;</div><div><br></div><div>... with “overwriting” taking 0 or more variadic arguments.</div><div><br></div><div>This allows easily, concisely composing an immutable instance of a type out of various components of other immutable instances of a type. I think this is an extremely powerful pattern, and many times I wish that I had it.&nbsp;</div><div><br></div><div>In the absence of this, devs are prone to just use mutable instance vars instead of using immutable instance constants, just so they don’t have to do a whole member-wise initializer every time they want to just change one property.</div><div><br></div><div>Just my $0.02.</div><div><br></div><div>If there is already a way to use these things like that, then I want to know it.</div><div><br></div><div><span style="background-color: rgba(255, 255, 255, 0);">As for “why would this really be useful”, “what are the real-world benefits”, etc. ... I feel like if you really have to ask this, then it’s not because you actually cannot see the obvious benefits—it’s because you hate America.&nbsp;</span></div><div><br></div><div>~ Jon Gilbert</div><div><br></div><div>On Aug 23, 2017, at 23:19, Logan Shire via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a>&gt; wrote:<br><br></div><blockquote type="cite"><div><span>Hey folks! </span><br><span></span><br><span>Recently I’ve been working on a small library which leverages the Swift 4 Codable protocol</span><br><span>and KeyPaths to provide a Swift-y interface to CoreData. (It maps back and forth between</span><br><span>native, immutable Swift structs and NSManagedObjects). In doing so I found a couple of </span><br><span>frustrating limitations to the KeyPath API. Firstly, KeyPath does not provide the name of the </span><br><span>property on the type it indexes. For example, if I have a struct:</span><br><span></span><br><span></span><br><span>struct Person {</span><br><span> &nbsp;&nbsp;&nbsp;let firstName: String</span><br><span> &nbsp;&nbsp;&nbsp;let lastName: String</span><br><span>}</span><br><span></span><br><span>let keyPath = \Person.firstName</span><br><span></span><br><span></span><br><span>But once I have a keyPath, I can’t actually figure out what property it accesses.</span><br><span>So, I wind up having to make a wrapper:</span><br><span></span><br><span></span><br><span>struct Attribute {</span><br><span> &nbsp;&nbsp;&nbsp;let keyPath: AnyKeyPath</span><br><span> &nbsp;&nbsp;&nbsp;let propertyName: String</span><br><span>}</span><br><span></span><br><span>let firstNameAttribute = Attribute(keyPath: \Person.firstName, propertyName: “firstName”)</span><br><span></span><br><span></span><br><span>This forces me to write out the property name myself as a string which is very error prone.</span><br><span>All I want is to be able to access:</span><br><span></span><br><span></span><br><span>keyPath.propertyName // “firstName”</span><br><span></span><br><span></span><br><span>It would also be nice if we provided the full path as a string as well:</span><br><span></span><br><span></span><br><span>keyPath.fullPath // “Person.firstName"</span><br><span></span><br><span></span><br><span>Also, if I want to get all of the attributes from a given Swift type, my options are to try to hack</span><br><span>something together with Mirrors, or forcing the type to declare a function / computed property</span><br><span>returning an array of all of its key path / property name pairings. I would really like to be able to </span><br><span>retrieve a type-erased array of any type’s key paths with:</span><br><span></span><br><span></span><br><span>let person = Person(firstName: “John”, lastName: “Doe”)</span><br><span>let keyPaths = Person.keyPaths</span><br><span>let firstNameKeyPath = keyPaths.first { $0.propertyName = “firstName” } as! KeyPath&lt;Person, String&gt;</span><br><span>let firstName = person[keypath: firstNameKeyPath] // “John"</span><br><span></span><br><span></span><br><span>And finally, without straying too far into Objective-C land, it would be nice if we could initialize key paths</span><br><span>with a throwing initializer.</span><br><span></span><br><span></span><br><span>let keyPath = try Person.keyPath(“firstName”) // KeyPath&lt;Person, String&gt; type erased to AnyKeyPath</span><br><span>let keyPath = AnyKeyPath(“Person.firstName”)</span><br><span></span><br><span></span><br><span>Let me know what you think about any / all of these suggestions!</span><br><span></span><br><span></span><br><span>Thanks,</span><br><span>Logan</span><br><span></span><br><span></span><br><span>_______________________________________________</span><br><span>swift-evolution mailing list</span><br><span><a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a></span><br><span><a href="https://lists.swift.org/mailman/listinfo/swift-evolution">https://lists.swift.org/mailman/listinfo/swift-evolution</a></span><br></div></blockquote></div></div></div></div></div></div></div></body></html>