SE-0249: Key Path Expressions as Functions

+1

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes

  • Does this proposal fit well with the feel and direction of Swift?

Yes

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

N/A

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick reading

I use exactly the same syntax as in the proposal, implemented as map and filter extensions. We should admit that it's not much different than the closure syntax though much more limited. I don't mind using it, but since it's just sugar it's worth making it as sweet as possible! The proposed implementation implicitly converts a key path to a closure, so why should it not be a trailing closure? I suggest to allow parentheses to be skipped:

users.map\.email
users.filter\.isAdmin

or

users.map \.email
users.filter \.isAdmin

Toolchains! Fresh toolchains for macOS and Linux! Get 'em while they're hot!

(My review: I think this syntax is an excellent idea and will definitely become part of my personal style. I hope testing doesn't turn up any showstoppers.)

8 Likes

This is not correct, it has been mentioned before, this proposal only lets you convert a function with one parameter to a keypath. That implies that there never will be $1 (except for the yet undecided WritableKeyPath which is not part of the proposal), or in other words \. already refers to Root / $0 argument and in case of a tuple \.0 is already KeyPath<Root, FirstTupleElem>.

1 Like

The problem here is you lose the ability to chain along:

users.filter(\.isAdmin).map(\.email)
users.filter\.isAdmin // ???

Line breaks could help here, but it'd be a lot of special casing to worry about.

4 Likes

Well, in the rarer cases when we need chaining we can use parentheses. It is the same restriction as with trailing closures in conditions. Skipping parentheses is quite in the spirit of Swift.

+1 Bring it on!

  • What is your evaluation of the proposal?

+1. Love it.

I’m mildly concerned about it only supporting keypath literals and not KeyPath values. The KeyPath-as-@callable future direction seems promising.

Let’s do make sure to do performance profiling early on to address Chris’s concerns. I don’t want this to suffer the same terrible fate as count(where:)!

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes. Though it is a minor convenience, the problem it solves is common, the solution is elegant, and the readability benefit is real.

  • Does this proposal fit well with the feel and direction of Swift?

It does. Swift has so many conveniences and sugarings for closures, this feels like a happy addition to the family.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Ruby’s person.map(&:name) is surprisingly pleasant to use.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Medium quick read, plus tracking earlier pitch threads.

  • What is your evaluation of the proposal?

+1

  • Is the problem being addressed significant enough to warrant a change to Swift?

Yes

  • Does this proposal fit well with the feel and direction of Swift?

Yes

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

N/A

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed the pitch, read the proposal and this thread

  • What is your evaluation of the proposal?
    +1

  • Is the problem being addressed significant enough to warrant a change to Swift?
    I believe so. It's a small but valuable improvement. I've defined such extensions for map and filter myself, so it's great to see a general solution.

  • Does this proposal fit well with the feel and direction of Swift?
    Yes. It gives keypath API a very common - almost every day - use case.

  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
    N/a.

  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    I've read the pitch thread and the proposal.

  • What is your evaluation of the proposal?
    +1
  • Is the problem being addressed significant enough to warrant a change to Swift?
    Yes, this will make key paths a much more useful feature
  • Does this proposal fit well with the feel and direction of Swift?
    Yes. We already have key paths, might as well make them (more) useful
  • If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
    n/a
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    I read through the pitch thread and the proposal.
  • What is your evaluation of the proposal?
    +1
  • Is the problem being addressed significant enough to warrant a change to Swift?
    Yes I would like to see this owned by Swift so it can be considered and optimized moving forward.
  • Does this proposal fit well with the feel and direction of Swift?
    Yes.
  • 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 &: in Ruby and this feels familiar.
  • How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
    A quick reading.

Source compatibility tests against the implementation as modified in #23435 looked good. Compiler performance showed a 0.05%–0.5% regression, which is small enough that it could easily be noise.

7 Likes

A big +1 for this proposal. It is a very targeted and elegant solution.

I hope to see the @callable direction flushed out in the future...

What is your evaluation of the proposal?

A very strong +1.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes. It just feels natural. I've been writing array.map(\.property) many times, which was followed by a recurring dissapointing realization that this is not implemented yet.

Does this proposal fit well with the feel and direction of Swift?

Definitely.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Read the proposal.

I support this proposal, but I'd like to see the following details clarified:

Resolve priority
The proposal doesn't mention overloading.

With the PR, the following is ambiguous:

class C {
    var foo: Int = 0
}

func take(_ arg: KeyPath<C, Int>) {}
func take(_ arg: (C) -> Int) {}

take(\C.foo)

I think the explicit KeyPath parameter should be preferred over the implicit conversion. (That would be consistent with explicit C being preferred over AnyObject or AnyHashable.)

Calling of key paths
The proposal doesn't make it clear if key paths can be called directly.

The PR allows the following case:

let _ = (\C.foo)(C())

I think there is no benefit to this and it's confusing, so I propose we don't allow it (at least until a general @callable is considered).

A disambiguation rule was added in the rebased version of the PR. (Specifically, the rule is that, when comparing two valid solutions, if one uses a subtype of AnyKeyPath where the other uses a function type, the key path solution's score is incremented so that it will be favored. This can apply to any function-typed value, not just one created with this proposal's syntax.)

4 Likes

Hi everyone,

The review period has now ended and this proposal has been accepted.

Thanks for your feedback during the review!

Ben Cohen
Review Manager

14 Likes

Hello, I would like to ask for the update on this proposal. I see that this proposal has been accepted but not been merged yet. Will it be merged in Swift 5.1?

1 Like

Maybe you just send a ping in the PR, which is here: https://github.com/apple/swift/pull/19448