Some small keypath extensions: identity and tuple components

\.self sounds clear enough to me: it's very similar to the self property available on types and both can be chained:

\.self.self.self.self == \.self
self.self.self.self == self
1 Like

But keypaths apply to instances, right? Objective-C has a self member for instances, but Swift does not.

print(1.self) works fine. Maybe that’s a keyword rather than a real property, but that’s real enough for me.

1 Like

Currently, it's allowed to declare

var `self`: T

Aside from the question wether that is a good idea or not, I'd prefer a solution that simply can't interfere with real properties.
Forbidding "self" as a member name would be one way around that issue, the other would be a string that can never be the name of a property.
\. is probably the most obvious one (and if Swift would use slashes instead of points for path separation, Unix guys would feel home right away ;-)

The quoted identifier `self` is different from the keyword self, though. I agree that \.self is not the ideal syntax, especially because with an explicit root type it looks like \T.self, which is only one character away from a type value reference T.self. Ideally, if we ever get around to revisiting SE-90, we would be able to kill the .self member entirely. \. is cute but maybe a little mysterious. What about a static member of AnyKeyPath?

extension AnyKeyPath {
  static func identity<T>() -> WritableKeyPath<T, T>
}

This would let you write .identity() in context where a KeyPath or any subclass thereof is expected.

1 Like

In a current discussion about single element tuples there was mention of this thread, and a suggestion to use \.0 as the identity keypath element, here.
This would effectively be coercing T to an unlabeled single element tuple (T) getting it's first element, and coercing back to T, except unlabeled single element tuples don't exist.
And even labeled single element tuples only exist in some parts of the compiler.

2 Likes

Personally, I think \. is short, sweet, and, erhm, to the point! More to the point, perhaps, it seems easy to come up with: it's the first thing I'd think of for how to spell the identity keypath, and I could remember it rather than having to google for how to spell it otherwise. When you first suggested this concept my first thought was "hmm, too bad it can't be spelled \. because that would be so obvious."

3 Likes

FWIW, \. was the first thing I have tried some time ago when I wanted the identity path and was wondering if that’s supported.

Agree, but it's worth considering. It is somewhat reminiscent of using . to refer to the current working directory. That analogy might be enough to help people remember what it means.

3 Likes

One theoretical downside to \., besides the cryptic nature, is it makes more partially-written code valid. Probably not a big deal in practice though, especially since keypaths are a somewhat niche feature at the moment.

4 Likes

I also like "." for the identity key path.

Since appending the identity key path is an identity operation, it seems this would be valid syntax:

let keyPath = \Person.....address......city......

It might be worth considering how the compiler treats expressions like that.

Can we add support for static members into the bucket of small extensions?

struct MyType {
  static var staticMember = "swift"
}

\MyType.Type.staticMember
1 Like

That would require some more infrastructure to support. I would prefer to focus on these two, as I noted in the original post.

4 Likes

No objection, but I‘d wish to see it in Swift 5 though. :slight_smile:

FWIW, I ran into a use case yesterday where the identity key path is exactly what I need. I can work around not having it, but it would be much better if I didn't have to. :slight_smile:

About small improvements to key paths, it would be nice to know if a key path is a property (like \Value.field) or a sub-property (like \Value.field.subField). And to be able to decompose a key path if it is a sub-property.

I needed that once, when I handled lazy-loading with key paths. To lazily load the sub-property of a value, you have to lazily load the property then the sub-property. So you need the decomposition of the key path.

I prefer a bare \ for the identity KeyPath. My rationale:

  • It feels like root, akin to the / directory
  • Unlike \., it obeys the rule that, textually, you append one KeyPath to another by adding a dot and the attribute name, e.g., the name of \.father is \.father.name. Accordingly, if you iteratively stripped away attributes in that KeyPath, you'd be left with \.father.name\.father\ .
3 Likes

Key path decomposition would be another thing that's straightforward to add to the current implementation, but I'd appreciate if we could discuss it as a separate topic.

1 Like

Since identity key paths would require some runtime support before ABI stability, I went ahead and implemented that backend support (without a finalized surface syntax) here:

15 Likes

Here's a formal proposal for the identity key path: Proposal for identity key paths by jckarter · Pull Request #895 · apple/swift-evolution · GitHub

4 Likes