Thanks. I misunderstood Rob's example code. I realized it and delete my question before I saw your reply.
have i missed anything?
As nominal types, key paths can be extended with protocol conformances, methods, and properties. Functions cannot be so extended. I haven't had an occasion to use that ability though (beyond the built-in conformances).
A fun thing I contemplated back in the day is "what if key paths could get conditional conformances based on attributes* of the properties along the path?". I think this might boil down to accidentally making a generalized effects system again, but it was a cute idea.
*for whatever definition of attributes.
A fun thing I contemplated back in the day is "what if key paths could get conditional conformances based on attributes* of the properties along the path?". I think this might boil down to accidentally making a generalized effects system again, but it was a cute idea.
*for whatever definition of attributes.
That should be possible now that we do support protocol compositions with base classes. WritableKeyPath
is already burned in as a subclass, it would definitely be more composable to introduce new capabilities like KeyPath<T, U> & Sendable & Codable
by protocol composition rather than by extending the class hierarchy. We also have more generalized existentials than we used to, so at some point it might be nice to supersede the KeyPath
class itself with a protocol now that we can express types like any KeyPathProtocol<T, U>
.
So, trying to boil this down to an elevator pitch on KeyPaths. What I have so far is:
KeyPaths allow you to hand over a reference to a member of a type, as opposed to the value of that member at the present time, which can be useful for when something need to know not only what value to work with, but also what member of that value is important. For instance, if you have a structure with several members, a KeyPath can be used to say which member ought to be used to sort with.
So, given:
struct Worker {
var name: String
var title: String
var division: String
}
func consider(worker: F, workerData: KeyPath<F, String>) {
print(worker[keyPath: workerData])
}
let x = F(name: "Jack", title: "Tradesman", division: "Mechs")
consider(follower: x, followerItem: \.division) // prints "Mechs"
Okay, that much makes sense to me.
does KeyPath
still use reference counting?
at some point it might be nice to supersede the
KeyPath
class itself with a protocol now that we can express types likeany KeyPathProtocol<T, U>
.
despite knowing exactly what AnyObject
-constrained existentials are, for some reason my brain still thinks of the any
keyword as “slower” than a polymorphic class instance. old prejudices die hard…
Another benefit of a protocol is that you could define types and functions generic over the protocol in order to store the information for a particular keypath inline without wrapping.
I use keypaths with map every single day, much clearer than using closures.
While "clearer" doesn't represent how I feel about \.
, $
is the most nonsense-looking, American-capitalism-touting, rump-ugliest thing in the language. It was bad enough with $0
—at least that could be avoided by supplying a name. Property wrappers + SwiftUI made it impossible.
Yeah, having suffered through the hardworking weirdness that is Perl for years as a sys-admin, I’m not a big fan of the use of sigils. This applies to Optionals as well. I’m used to it, but that’s as far as I’ll go.