Smart KeyPaths

I would suggest a keypath using ~ which is concise and clearer to identify.

let myPath = Person~friends[0].name

···

On Sunday, March 19, 2017, Jonathan Hull via swift-evolution < swift-evolution@swift.org> wrote:

This looks fantastic!

The one worry I would have, as others have brought up, is confusion when
there are static properties with the same name. Maybe we could have a
special static property called ‘keyPath’ or ‘path’ that would be reserved,
and anything that followed would be a key path.

So instead of:

let myPath = Person.friends[0].name

you would have:

let myPath = Person.keyPath.friends[0].name

Or if we want to choose a sentinel, I would nominate ‘$’:

let myPath = $Person.friends[0].name

Thanks,
Jon

On Mar 17, 2017, at 10:04 AM, Michael LeHew via swift-evolution < > swift-evolution@swift.org > <javascript:_e(%7B%7D,'cvml','swift-evolution@swift.org');>> wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the
following proposal:

Many thanks,
-Michael

Smart KeyPaths: Better Key-Value Coding for Swift

   - Proposal: SE-NNNN
   - Authors: David Smith <https://github.com/Catfish-Man&gt;, Michael LeHew
   <https://github.com/mlehew&gt;, Joe Groff <https://github.com/jckarter&gt;
   - Review Manager: TBD
   - Status: *Awaiting Review*
   - Associated PRs:
      - #644 <https://github.com/apple/swift-evolution/pull/644&gt;

Introduction

We propose a family of concrete *Key Path* types that represent uninvoked
references to properties that can be composed to form paths through many
values and directly get/set their underlying values.
MotivationWe Can Do Better than String

On Darwin platforms Swift's existing keypath() syntax provides a
convenient way to safely *refer* to properties. Unfortunately, once
validated, the expression becomes a String which has a number of
important limitations:

   - Loss of type information (requiring awkward Any APIs)
   - Unnecessarily slow to parse
   - Only applicable to NSObjects
   - Limited to Darwin platforms

Use/Mention Distinctions

While methods can be referred to without invoking them (let x = foo.bar instead
of let x = foo.bar()), this is not currently possible for properties and
subscripts.

Making indirect references to a properties' concrete types also lets us
expose metadata about the property, and in the future additional behaviors.
More Expressive KeyPaths

We would also like to support being able to use *Key Paths* to access
into collections, which is not currently possible.
Proposed solution

We propose introducing a new expression akin to Type.method, but for
properties and subscripts. These property reference expressions produce
KeyPath objects, rather than Strings. KeyPaths are a family of generic
classes *(structs and protocols here would be ideal, but requires
generalized existentials)* which encapsulate a property reference or
chain of property references, including the type, mutability, property
name(s), and ability to set/get values.

Here's a sample of it in use:
Swift

struct Person {
    var name: String
    var friends: [Person]
    var bestFriend: Person?}
var han = Person(name: "Han Solo", friends: )var luke = Person(name: "Luke Skywalker", friends: [han])
let firstFriendsNameKeyPath = Person.friends[0].name
let firstFriend = luke[path] // han
// or equivalently, with type inferred from contextlet firstFriendName = luke[.friends[0].name]
// rename Luke's first friend
luke[firstFriendsNameKeyPath] = "A Disreputable Smuggler"
let bestFriendsName = luke[.bestFriend]?.name // nil, if he is the last jedi

Detailed designCore KeyPath Types

KeyPaths are a hierarchy of progressively more specific classes, based on
whether we have prior knowledge of the path through the object graph we
wish to traverse.
Unknown Path / Unknown Root Type

AnyKeyPath is fully type-erased, referring to 'any route' through an
object/value graph for 'any root'. Because of type-erasure many operations
can fail at runtime and are thus nillable.
Swift

class AnyKeyPath: CustomDebugStringConvertible, Hashable {
    // MARK - Composition
    // Returns nil if path.rootType != self.valueType
    func appending(path: AnyKeyPath) -> AnyKeyPath?

    // MARK - Runtime Information
    class var rootType: Any.Type
    class var valueType: Any.Type

    static func == (lhs: AnyKeyPath, rhs: AnyKeyPath) -> Bool
    var hashValue: Int}

Unknown Path / Known Root Type

If we know a little more type information (what kind of thing the key path
is relative to), then we can use PartialKeyPath <Root>, which refers to
an 'any route' from a known root:
Swift

class PartialKeyPath<Root>: AnyKeyPath {
    // MARK - Composition
    // Returns nil if Value != self.valueType
    func appending(path: AnyKeyPath) -> PartialKeyPath<Root>?
    func appending<Value, AppendedValue>(path: KeyPath<Value, AppendedValue>) -> KeyPath<Root, AppendedValue>?
    func appending<Value, AppendedValue>(path: ReferenceKeyPath<Value, AppendedValue>) -> ReferenceKeyPath<Root, AppendedValue>?}

Known Path / Known Root Type

When we know both what the path is relative to and what it refers to, we
can use KeyPath. Thanks to the knowledge of the Root and Value types, all
of the failable operations lose their Optional.
Swift

public class KeyPath<Root, Value>: PartialKeyPath<Root> {
    // MARK - Composition
    func appending<AppendedValue>(path: KeyPath<Value, AppendedValue>) -> KeyPath<Root, AppendedValue>
    func appending<AppendedValue>(path: WritableKeyPath<Value, AppendedValue>) -> Self
    func appending<AppendedValue>(path: ReferenceWritableKeyPath<Value, AppendedValue>) -> ReferenceWritableKeyPath<Root, AppendedValue>}

Value/Reference Mutation Semantics Mutation

Finally, we have a pair of subclasses encapsulating value/reference
mutation semantics. These have to be distinct because mutating a copy of a
value is not very useful, so we need to mutate an inout value.
Swift

class WritableKeyPath<Root, Value>: KeyPath<Root, Value> {
    // MARK - Composition
    func appending<AppendedPathValue>(path: WritableKeyPath<Value, AppendedPathValue>) -> WritableKeyPath<Root, AppendedPathValue>}
class ReferenceWritableKeyPath<Root, Value>: WritableKeyPath<Root, Value> {
    override func appending<AppendedPathValue>(path: WritableKeyPath<Value, AppendedPathValue>) -> ReferenceWritableKeyPath<Root, AppendedPathValue>}

Access and Mutation Through KeyPaths

To get or set values for a given root and key path we effectively add the
following subscripts to all Swift types.
Swift

extension Any {
    subscript(path: AnyKeyPath) -> Any? { get }
    subscript<Root: Self>(path: PartialKeyPath<Root>) -> Any { get }
    subscript<Root: Self, Value>(path: KeyPath<Root, Value>) -> Value { get }
    subscript<Root: Self, Value>(path: WritableKeyPath<Root, Value>) -> Value { set, get }}

This allows for code like
Swift

person[.name] // Self.type is inferred

which is both appealingly readable, and doesn't require read-modify-write
copies (subscripts access self inout). Conflicts with existing subscripts
are avoided by using generic subscripts to specifically only accept key
paths with a Root of the type in question.
Referencing Key Paths

Forming a KeyPath borrows from the same syntax used to reference methods
and initializers,Type.instanceMethod only now working for properties and
collections. Optionals are handled via optional-chaining. Multiply dotted
expressions are allowed as well, and work just as if they were composed via
the appending methods on KeyPath.

There is no change or interaction with the keypath() syntax introduced in
Swift 3.
Performance

The performance of interacting with a property via KeyPaths should be
close to the cost of calling the property directly.
Source compatibility

This change is additive and there should no affect on existing source.
Effect on ABI stability

This feature adds the following requirements to ABI stability:

   - mechanism to access key paths of public properties

We think a protocol-based design would be preferable once the language has
sufficient support for generalized existentials to make that ergonomic. By
keeping the class hierarchy closed and the concrete implementations private
to the implementation it should be tractable to provide compatibility with
an open protocol-based design in the future.
Effect on API resilience

This should not significantly impact API resilience, as it merely provides
a new mechanism for operating on existing APIs.
Alternatives consideredMore Features

Various drafts of this proposal have included additional features
(decomposable key paths, prefix comparisons, support for custom

There won't be a conversion from strings in this initial proposal. That would be a reasonable thing to add later. For the use case of formatting strings, hopefully, there'll eventually be some way to build formatting templates that are well-typed instead of relying on parsing strings at runtime.

-Joe

···

On Mar 20, 2017, at 4:01 PM, Kenny Leung via swift-evolution <swift-evolution@swift.org> wrote:

Hi All.

I’m not sure I’m understanding this proposal properly. In (old) Cocoa, two places where key paths were used extensively was EOF/CoreData, and WebObjects. I’m wondering how Smart KeyPaths will solve these two problems:

1. fetching data from a database and stuff it into objects that are not known at compile time (since you’ve written the framework ahead of time)

2. Token replacing text in a template, like ${person.firstName}

Will there be some conversion of key paths to/from strings?

Thanks for the feedback everyone! We have pushed a changed a bit ago to the proposal reflecting these desires.

-Michael

···

On Mar 29, 2017, at 2:49 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Mar 17, 2017, at 10:04 AM, Michael LeHew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the following proposal:

The Swift core team discussed this proposal draft and had a little bit of pre-review feedback.

Access and Mutation Through KeyPaths
To get or set values for a given root and key path we effectively add the following subscripts to all Swift types.

Swift
extension Any {
    subscript(path: AnyKeyPath) -> Any? { get }
    subscript<Root: Self>(path: PartialKeyPath<Root>) -> Any { get }
    subscript<Root: Self, Value>(path: KeyPath<Root, Value>) -> Value { get }
    subscript<Root: Self, Value>(path: WritableKeyPath<Root, Value>) -> Value { set, get }
}

Swift doesn’t currently have the ability to extend Any, so this is (currently) pseudocode for compiler magic that one day we might be able to place. Additionally, the “Root: Self” constraint isn’t something we support in the generics system. A small note indicating that this is pseudo-code meant to get the point across (rather than real code to drop into the standard library/Foundation) would be appreciated.

More importantly, this adds an unlabeled subscript to every type, which raises concerns about introducing ambiguities—even if not hard ambiguities that prevent code from compiling (e.g., from a Dictionary<AnyKeyPath, …>)---they can still show up in code completion, diagnostics, etc.

The core team would prefer that this subscript distinguish itself more, e.g., by labeling the first parameter “keyPath” (or some better name, if there is one). Syntactically, that would look like:

  person[keyPath: theKeyPathIHave]

Referencing Key Paths

Forming a KeyPath borrows from the same syntax used to reference methods and initializers,Type.instanceMethod only now working for properties and collections. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the appending methods on KeyPath.

The core team was concerned about the use of the Type.instanceProperty syntax for a few reasons:

  * It doesn’t work for forming keypaths to class/static properties (or is ambiguous with the existing meaning(, so we would need another syntax to deal with that case
  * It’s quite subtle, even more so that the existing Type.instanceMethod syntax for currying instance methods

There is no change or interaction with the keypath() syntax introduced in Swift 3.

The core team felt that extending the keypath syntax was a better syntactic direction to produce key-paths.

  - Doug

So then this would be disambiguated like this?

let staticValue = Foo.bar // Defaults to accessing the static value, when there is ambiguity

let value: Bar = Foo.bar
let keyPath: WritableKeyPath<Foo, Bar> = Foo.bar

It’s a little unfortunately to have to spell out WritableKeyPath<Foo, Bar> there, but as long as there’s some way to do it, I don’t think it’s a problem. This is likely a rare edge case.

You could also just write `: WritableKeyPath` and let the generic arguments be deduced. I agree that, in most situations you want a key path, you'll likely have type context that picks the right thing.

-Joe

···

On Mar 17, 2017, at 3:04 PM, BJ Homer <bjhomer@gmail.com> wrote:

-BJ Homer

On Mar 17, 2017, at 3:56 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

On Mar 17, 2017, at 2:53 PM, Michael LeHew <lehewjr@apple.com <mailto:lehewjr@apple.com>> wrote:

On Mar 17, 2017, at 2:21 PM, BJ Homer <bjhomer@gmail.com <mailto:bjhomer@gmail.com>> wrote:

This looks great!

What happens in the case when there is a static property by the same name as an instance property? e.g.

struct Foo {
    static var bar: Bar
    var bar: Bar
}

Foo.bar // What is this?

Is it still possible to reference both the static property and a KeyPath to the instance method?

This is essentially the same question that I arrived at in my reply to Vladimir. I think Joe might be best able to answer here.

We already encounter this situation with static vs instance methods, since `Foo.bar` can refer to either a static method `bar` or an unbound instance method `bar`. We use type context to disambiguate, favoring the static member if context doesn't help:

struct X {
  static func foo() {}
  func foo() {}
}

let foo1 = X.foo // Defaults to static member
let foo2: () -> () = X.foo // Picks static member by type context
let foo3: (X) -> () -> () = X.foo // Picks instance member by type context

-Joe

Introduction
We propose a family of concrete Key Path types that represent uninvoked references to properties that can be composed to form paths through many values and directly get/set their underlying values.

I don't know how to express the level of "Yes" I feel in response to this proposal without using language that's inappropriate on a public mailing list.

A few questions:

How do key paths interact with Optionals? Can you form a key path to `Person.bestFriend.name`, and is that the syntax, or is it `Person.bestFriend?.name`?

Foundation key paths have a sometimes-useful property where, if they traverse a collection, the result becomes a collection of the ending type. Is a similar feature planned for smart key paths—perhaps something like `Person.friends.name`?

In many cases, you could add a property or subscript operation that has the same effect, e.g.:

extension Array {
  subscript<NewElement>(mapping key: KeyPath<Element, NewElement>) -> Array<NewElement> {
    return map { $0[key] }
  }
}

A non-trivial benefit that's easy to overlook with this kind of approach (vs more ad-hoc closure transformed KeyPaths) is that the contract and capabilities are part of the API surface of your type and thus more easily testable.

···

On Mar 17, 2017, at 3:57 PM, Joe Groff <jgroff@apple.com> wrote:

On Mar 17, 2017, at 1:58 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Mar 17, 2017, at 10:04 AM, Michael LeHew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

would let you write `Person.friends[mapping: .name]`. Before publishing the proposal, we had discussed having closure-based key path components. However, as Michael noted, an important capability of key paths in ObjC is that, while abstractly they're "just functions" in a sense, they're also introspectable and serializable values. Closures in Swift are by design opaque, so can't be equated, hashed, traversed by component, or serialized. Keeping key paths attached to declarations lets them preserve that structure, enabling their use for more interesting things like KVO in the future.

-Joe

Given that Swift has a syntax called keypath which is unrelated to these "key paths", have you considered using a different name to avoid confusion? Maybe "property paths" or "accessor paths"?

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Would it be possible to create operators?

let total = manager[@sum.reports.salary]

···

On Mar 18, 2017, at 22:00 , Muse M via swift-evolution <swift-evolution@swift.org> wrote:

I would suggest a keypath using ~ which is concise and clearer to identify.

  let myPath = Person~friends[0].name

On Sunday, March 19, 2017, Jonathan Hull via swift-evolution <swift-evolution@swift.org> wrote:
This looks fantastic!

The one worry I would have, as others have brought up, is confusion when there are static properties with the same name. Maybe we could have a special static property called ‘keyPath’ or ‘path’ that would be reserved, and anything that followed would be a key path.

So instead of:

  let myPath = Person.friends[0].name

you would have:

  let myPath = Person.keyPath.friends[0].name

Or if we want to choose a sentinel, I would nominate ‘$’:

  let myPath = $Person.friends[0].name

Thanks,
Jon

On Mar 17, 2017, at 10:04 AM, Michael LeHew via swift-evolution <swift-evolution@swift.org> wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the following proposal:

Many thanks,
-Michael

Smart KeyPaths: Better Key-Value Coding for Swift
  • Proposal: SE-NNNN
  • Authors: David Smith, Michael LeHew, Joe Groff
  • Review Manager: TBD
  • Status: Awaiting Review
  • Associated PRs:
    • #644
Introduction
We propose a family of concrete Key Path types that represent uninvoked references to properties that can be composed to form paths through many values and directly get/set their underlying values.

Motivation
We Can Do Better than String

On Darwin platforms Swift's existing keypath() syntax provides a convenient way to safely refer to properties. Unfortunately, once validated, the expression becomes a String which has a number of important limitations:

  • Loss of type information (requiring awkward Any APIs)
  • Unnecessarily slow to parse
  • Only applicable to NSObjects
  • Limited to Darwin platforms
Use/Mention Distinctions

While methods can be referred to without invoking them (let x = foo.bar instead of let x = foo.bar()), this is not currently possible for properties and subscripts.

Making indirect references to a properties' concrete types also lets us expose metadata about the property, and in the future additional behaviors.

More Expressive KeyPaths

We would also like to support being able to use Key Paths to access into collections, which is not currently possible.

Proposed solution
We propose introducing a new expression akin to Type.method, but for properties and subscripts. These property reference expressions produce KeyPath objects, rather than Strings. KeyPaths are a family of generic classes (structs and protocols here would be ideal, but requires generalized existentials) which encapsulate a property reference or chain of property references, including the type, mutability, property name(s), and ability to set/get values.

Here's a sample of it in use:

Swift
struct Person {

var name: String

var friends: [Person]

var bestFriend: Person?
}

var han = Person(name: "Han Solo", friends: )
var luke = Person(name: "Luke Skywalker", friends: [han])

let firstFriendsNameKeyPath = Person.friends[0].
name

let firstFriend = luke[path] // han

// or equivalently, with type inferred from context
let firstFriendName = luke[.friends[0].name]

// rename Luke's first friend

luke
[firstFriendsNameKeyPath] = "A Disreputable Smuggler"

let bestFriendsName = luke[.bestFriend]?.name // nil, if he is the last jedi
Detailed design
Core KeyPath Types

KeyPaths are a hierarchy of progressively more specific classes, based on whether we have prior knowledge of the path through the object graph we wish to traverse.

Unknown Path / Unknown Root Type

AnyKeyPath is fully type-erased, referring to 'any route' through an object/value graph for 'any root'. Because of type-erasure many operations can fail at runtime and are thus nillable.

Swift
class AnyKeyPath: CustomDebugStringConvertible, Hashable {

// MARK - Composition

// Returns nil if path.rootType != self.valueType

func appending(path: AnyKeyPath) -> AnyKeyPath?

// MARK - Runtime Information

class var rootType: Any.Type

class var valueType: Any.Type

static func == (lhs: AnyKeyPath, rhs: AnyKeyPath) -> Bool

var hashValue: Int
}
Unknown Path / Known Root Type

If we know a little more type information (what kind of thing the key path is relative to), then we can use PartialKeyPath <Root>, which refers to an 'any route' from a known root:

Swift
class PartialKeyPath<Root>: AnyKeyPath {

// MARK - Composition

// Returns nil if Value != self.valueType

func appending(path: AnyKeyPath) -> PartialKeyPath<Root>?

func appending<Value, AppendedValue>(path: KeyPath<Value, AppendedValue>) -> KeyPath<Root, AppendedValue>?

func appending<Value, AppendedValue>(path: ReferenceKeyPath<Value, AppendedValue>) -> ReferenceKeyPath<Root, AppendedValue>?
}
Known Path / Known Root Type

When we know both what the path is relative to and what it refers to, we can use KeyPath. Thanks to the knowledge of the Root and Value types, all of the failable operations lose their Optional.

Swift
public class KeyPath<Root, Value>: PartialKeyPath<Root> {

// MARK - Composition

func appending<AppendedValue>(path: KeyPath<Value, AppendedValue>) -> KeyPath<Root, AppendedValue>

func appending<AppendedValue>(path: WritableKeyPath<Value, AppendedValue>) -> Self

func appending<AppendedValue>(path: ReferenceWritableKeyPath<Value, AppendedValue>) -> ReferenceWritableKeyPath<Root, AppendedValue>
}
Value/Reference Mutation Semantics Mutation

Finally, we have a pair of subclasses encapsulating value/reference mutation semantics. These have to be distinct because mutating a copy of a value is not very useful, so we need to mutate an inout value.

Swift
class WritableKeyPath<Root, Value>: KeyPath<Root, Value> {

// MARK - Composition

func appending<AppendedPathValue>(path: WritableKeyPath<Value, AppendedPathValue>) -> WritableKeyPath<Root, AppendedPathValue>
}

class ReferenceWritableKeyPath<Root, Value>: WritableKeyPath<Root, Value> {

override func appending<AppendedPathValue>(path: WritableKeyPath<Value, AppendedPathValue>) -> ReferenceWritableKeyPath<Root, AppendedPathValue>
}
Access and Mutation Through KeyPaths

To get or set values for a given root and key path we effectively add the following subscripts to all Swift types.

Swift
extension Any {

subscript(path: AnyKeyPath) -> Any? { get }

subscript<Root: Self>(path: PartialKeyPath<Root>) -> Any { get }

subscript<Root: Self, Value>(path: KeyPath<Root, Value>) -> Value { get }

subscript<Root: Self, Value>(path: WritableKeyPath<Root, Value>) -> Value { set, get }
}
This allows for code like

Swift
person[.name] // Self.type is inferred
which is both appealingly readable, and doesn't require read-modify-write copies (subscripts access self inout). Conflicts with existing subscripts are avoided by using generic subscripts to specifically only accept key paths with a Root of the type in question.

Referencing Key Paths

Forming a KeyPath borrows from the same syntax used to reference methods and initializers,Type.instanceMethod only now working for properties and collections. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the appending methods on KeyPath.

There is no change or interaction with the keypath() syntax introduced in Swift 3.

Performance

The performance of interacting with a property via KeyPaths should be close to the cost of calling the property directly.

Source compatibility
This change is additive and there should no affect on existing source.

Effect on ABI stability
This feature adds the following requirements to ABI stability:

  • mechanism to access key paths of public properties
We think a protocol-based design would be preferable once the language has sufficient support for generalized existentials to make that ergonomic. By keeping the class hierarchy closed and the concrete implementations private to the implementation it should be tractable to provide compatibility with an open protocol-based design in the future.

Effect on API resilience
This should not significantly impact API resilience, as it merely provides a new mechanism for operating on existing APIs.

Alternatives considered
More Features

Various drafts of this proposal have included additional features (decomposable key paths, prefix comparisons, support for custom

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Rick Mann
rmann@latencyzero.com

I might point out that this form of bikeshedding was already explicitly
called out in the text of the proposal. ("Alternatives Considered")
Your personal preferences as to what sigils are "lightweight" or "easy"
are not absolute ones without some form of evidence or objective
rationale provided.

Until that can be done, it would be more productive to discuss what we
can do to avoid ambiguity without magic sigils; or, in the face of Joe
Groff's post, why that ambiguity is a problem at all.

Best,

  Zachary Waldowski

  zach@waldowski.me

···

On Sun, Mar 19, 2017, at 01:00 AM, Muse M via swift-evolution wrote:

I would suggest a keypath using ~ which is concise and clearer to
identify.

let myPath = Person~friends[0].name

On Sunday, March 19, 2017, Jonathan Hull via swift-evolution <swift- > evolution@swift.org> wrote:

This looks fantastic!

The one worry I would have, as others have brought up, is confusion
when there are static properties with the same name. Maybe we could
have a special static property called ‘keyPath’ or ‘path’ that would
be reserved, and anything that followed would be a key path.

So instead of:

let myPath = Person.friends[0].name

you would have:

let myPath = Person.keyPath.friends[0].name

Or if we want to choose a sentinel, I would nominate ‘$’:

let myPath = $Person.friends[0].name

Thanks,

Jon

On Mar 17, 2017, at 10:04 AM, Michael LeHew via swift-evolution <swift- >>> evolution@swift.org> wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the
following proposal:

Many thanks,

-Michael

Smart KeyPaths: Better Key-Value Coding for Swift

* Proposal: SE-NNNN
* Authors: David Smith[1], Michael LeHew[2], Joe Groff[3]
* Review Manager: TBD
* Status: *Awaiting Review*
* Associated PRs:
   * #644[4]
Introduction

We propose a family of concrete *Key Path* types that represent
uninvoked references to properties that can be composed to form
paths through many values and directly get/set their underlying
values.
Motivation

We Can Do Better than String

On Darwin platforms Swift's existing keypath() syntax provides a
convenient way to safely *refer* to properties. Unfortunately, once
validated, the expression becomes a String which has a number of
important limitations:

* Loss of type information (requiring awkward Any APIs)
* Unnecessarily slow to parse
* Only applicable to NSObjects
* Limited to Darwin platforms
Use/Mention Distinctions

While methods can be referred to without invoking them (let x =
foo.bar instead of let x = foo.bar()), this is not currently
possible for properties and subscripts.
Making indirect references to a properties' concrete types also lets
us expose metadata about the property, and in the future additional
behaviors.
More Expressive KeyPaths

We would also like to support being able to use *Key Paths* to
access into collections, which is not currently possible.
Proposed solution

We propose introducing a new expression akin to Type.method, but for
properties and subscripts. These property reference expressions
produce KeyPath objects, rather than Strings. KeyPaths are a family
of generic classes *(structs and protocols here would be ideal, but
requires generalized existentials)* which encapsulate a property
reference or chain of property references, including the type,
mutability, property name(s), and ability to set/get values.
Here's a sample of it in use:

Swift struct Person { var name: String var friends: [Person] var
bestFriend: Person? }

var han = Person(name: "Han Solo", friends: ) var luke =
Person(name: "Luke Skywalker", friends: [han])

let firstFriendsNameKeyPath = Person.friends.name

let firstFriend = luke[path] // han

// or equivalently, with type inferred from context let
firstFriendName = luke[.friends.name]

// rename Luke's first friend luke[firstFriendsNameKeyPath] = "A
Disreputable Smuggler"

let bestFriendsName = luke[.bestFriend]?.name // nil, if he is the
last jedi
Detailed design

Core KeyPath Types

KeyPaths are a hierarchy of progressively more specific classes,
based on whether we have prior knowledge of the path through the
object graph we wish to traverse.
Unknown Path / Unknown Root Type

AnyKeyPath is fully type-erased, referring to 'any route' through an
object/value graph for 'any root'. Because of type-erasure many
operations can fail at runtime and are thus nillable.
Swift class AnyKeyPath: CustomDebugStringConvertible, Hashable { //
MARK - Composition // Returns nil if path.rootType !=
self.valueType func appending(path: AnyKeyPath) -> AnyKeyPath? //
MARK - Runtime Information class var rootType: Any.Type
class var valueType: Any.Type static func == (lhs: AnyKeyPath,
rhs: AnyKeyPath) -> Bool var hashValue: Int }
Unknown Path / Known Root Type

If we know a little more type information (what kind of thing the
key path is relative to), then we can use PartialKeyPath <Root>,
which refers to an 'any route' from a known root:
Swift class PartialKeyPath<Root>: AnyKeyPath { // MARK -
Composition // Returns nil if Value != self.valueType func
appending(path: AnyKeyPath) -> PartialKeyPath<Root>? func
appending<Value, AppendedValue>(path: KeyPath<Value, AppendedValue>)
-> KeyPath<Root, AppendedValue>? func appending<Value,
>(path: ReferenceKeyPath<Value, AppendedValue>) ->
ReferenceKeyPath<Root, AppendedValue>? }
Known Path / Known Root Type

When we know both what the path is relative to and what it refers
to, we can use KeyPath. Thanks to the knowledge of the Root and
Value types, all of the failable operations lose their Optional.
Swift public class KeyPath<Root, Value>: PartialKeyPath<Root> { //
MARK - Composition func appending<AppendedValue>(path:
KeyPath<Value, AppendedValue>) -> KeyPath<Root, AppendedValue> func
appending<AppendedValue>(path: WritableKeyPath<Value,
>) -> Self func appending<AppendedValue>(path:
ReferenceWritableKeyPath<Value, AppendedValue>) ->
ReferenceWritableKeyPath<Root, AppendedValue> }
Value/Reference Mutation Semantics Mutation

Finally, we have a pair of subclasses encapsulating value/reference
mutation semantics. These have to be distinct because mutating a
copy of a value is not very useful, so we need to mutate an inout
value.
Swift class WritableKeyPath<Root, Value>: KeyPath<Root, Value> { //
MARK - Composition func appending<AppendedPathValue>(path:
WritableKeyPath<Value, AppendedPathValue>) -> WritableKeyPath<Root,
> }

class ReferenceWritableKeyPath<Root, Value>: WritableKeyPath<Root,
> { override func appending<AppendedPathValue>(path:
WritableKeyPath<Value, AppendedPathValue>) ->
ReferenceWritableKeyPath<Root, AppendedPathValue> }
Access and Mutation Through KeyPaths

To get or set values for a given root and key path we effectively
add the following subscripts to all Swift types.
Swift extension Any { subscript(path: AnyKeyPath) -> Any? { get }
subscript<Root: Self>(path: PartialKeyPath<Root>) -> Any { get }
subscript<Root: Self, Value>(path: KeyPath<Root, Value>) -> Value {
get } subscript<Root: Self, Value>(path: WritableKeyPath<Root,
>) -> Value { set, get } }
This allows for code like

Swift
person[.name] // Self.type is inferred

which is both appealingly readable, and doesn't require read-modify-
write copies (subscripts access self inout). Conflicts with existing
subscripts are avoided by using generic subscripts to specifically
only accept key paths with a Root of the type in question.
Referencing Key Paths

Forming a KeyPath borrows from the same syntax used to reference
methods and initializers,Type.instanceMethod only now working for
properties and collections. Optionals are handled via optional-
chaining. Multiply dotted expressions are allowed as well, and work
just as if they were composed via the appending methods on KeyPath.
There is no change or interaction with the keypath() syntax
introduced in Swift 3.
Performance

The performance of interacting with a property via KeyPaths should
be close to the cost of calling the property directly.
Source compatibility

This change is additive and there should no affect on existing
source.
Effect on ABI stability

This feature adds the following requirements to ABI stability:

* mechanism to access key paths of public properties
We think a protocol-based design would be preferable once the
language has sufficient support for generalized existentials to make
that ergonomic. By keeping the class hierarchy closed and the
concrete implementations private to the implementation it should be
tractable to provide compatibility with an open protocol-based
design in the future.
Effect on API resilience

This should not significantly impact API resilience, as it merely
provides a new mechanism for operating on existing APIs.
Alternatives considered

More Features

Various drafts of this proposal have included additional features
(decomposable key paths, prefix comparisons, support for custom

_________________________________________________

swift-evolution mailing list

swift-evolution@swift.org

https://lists.swift.org/mailman/listinfo/swift-evolution

Links:

  1. Catfish-Man (David Smith) · GitHub
  2. mlehew (Michael LeHew) · GitHub
  3. jckarter (Joe Groff) · GitHub
  4. Proposal for Keypath by michael-lehew · Pull Request #644 · apple/swift-evolution · GitHub

Thanks to both of you for pointing how to disambiguate and how generic types can be inferred. I now think that this is the way to go. It looks to me like it is the simplest and the most elegant solution.

···

On Mar 17, 2017, at 6:05 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 17, 2017, at 3:04 PM, BJ Homer <bjhomer@gmail.com <mailto:bjhomer@gmail.com>> wrote:

So then this would be disambiguated like this?

let staticValue = Foo.bar // Defaults to accessing the static value, when there is ambiguity

let value: Bar = Foo.bar
let keyPath: WritableKeyPath<Foo, Bar> = Foo.bar

It’s a little unfortunately to have to spell out WritableKeyPath<Foo, Bar> there, but as long as there’s some way to do it, I don’t think it’s a problem. This is likely a rare edge case.

You could also just write `: WritableKeyPath` and let the generic arguments be deduced. I agree that, in most situations you want a key path, you'll likely have type context that picks the right thing.

-Joe

-BJ Homer

On Mar 17, 2017, at 3:56 PM, Joe Groff <jgroff@apple.com <mailto:jgroff@apple.com>> wrote:

On Mar 17, 2017, at 2:53 PM, Michael LeHew <lehewjr@apple.com <mailto:lehewjr@apple.com>> wrote:

On Mar 17, 2017, at 2:21 PM, BJ Homer <bjhomer@gmail.com <mailto:bjhomer@gmail.com>> wrote:

This looks great!

What happens in the case when there is a static property by the same name as an instance property? e.g.

struct Foo {
    static var bar: Bar
    var bar: Bar
}

Foo.bar // What is this?

Is it still possible to reference both the static property and a KeyPath to the instance method?

This is essentially the same question that I arrived at in my reply to Vladimir. I think Joe might be best able to answer here.

We already encounter this situation with static vs instance methods, since `Foo.bar` can refer to either a static method `bar` or an unbound instance method `bar`. We use type context to disambiguate, favoring the static member if context doesn't help:

struct X {
  static func foo() {}
  func foo() {}
}

let foo1 = X.foo // Defaults to static member
let foo2: () -> () = X.foo // Picks static member by type context
let foo3: (X) -> () -> () = X.foo // Picks instance member by type context

-Joe

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I agree that they can get mixed up with static properties. However, I think I would not mind because it would only cause an ambiguity when having a static property with the same name as the property which would be an odd practice I think.

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

Without context, the following statement is ambiguous, This ambiguity can easily be avoid by choosing an other syntax. So why would we want to introduce it in the first place ?

let name = Person:friends[0].name

···

Le 19 mars 2017 à 16:41, Ricardo Parada via swift-evolution <swift-evolution@swift.org> a écrit :

I was defining static properties with the same name as the property in order to smart key paths. For example:

Class Person {
    static firstName: KeyPath<String>(“firstName”)
    static lastName: KeyPath<String>(“lastName”)

    var firstName: String
    var lastName: String

    …
}

So that I could create qualifiers, ie. Person.firstName.like(“R*”), and sort orderings, Person.firstName.asc().

I think with these smart key paths the need for these static properties that mirror the property on the instance would not be required.

On Mar 17, 2017, at 2:27 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

On 17.03.2017 20:04, Michael LeHew via swift-evolution wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the following
proposal:

Many thanks,
-Michael

Smart KeyPaths: Better Key-Value Coding for Swift
...

Just my 2 cents. FWIW, I feel like the proposed syntax is VERY confusing.

Person.friends[0].name - like access to static property. T.method can't have subscript for it and then some field. So, I believe such key paths deserve its own syntax.

luke[path] - very close to access the subscript of instance. In compare to T.method, we can call result of T.method(i.e. let m = T.method; m()), just like the T.method itself(foo.method()). But here we are calling some kind of subscript instead of access property by "path".

There is no some 'special' marker for me that saying "hey, there is references to properties used" from the first look.

Probably we can consider another separators, like

Person:friends[0].name
luke:path

Person->friends[0].name
luke->path

Person[friends[0].name] // single brackets for type
luke[[path]] // double brackets for instance

Person@friends[0].name
luke@path

or other with help of community.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

That is very interesting: "formatting templates that are well typed."

Would that be like a template and keypaths that were archived to a file, similar to a .nib file?

···

On Mar 20, 2017, at 7:28 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 20, 2017, at 4:01 PM, Kenny Leung via swift-evolution <swift-evolution@swift.org> wrote:

Hi All.

I’m not sure I’m understanding this proposal properly. In (old) Cocoa, two places where key paths were used extensively was EOF/CoreData, and WebObjects. I’m wondering how Smart KeyPaths will solve these two problems:

1. fetching data from a database and stuff it into objects that are not known at compile time (since you’ve written the framework ahead of time)

2. Token replacing text in a template, like ${person.firstName}

Will there be some conversion of key paths to/from strings?

There won't be a conversion from strings in this initial proposal. That would be a reasonable thing to add later. For the use case of formatting strings, hopefully, there'll eventually be some way to build formatting templates that are well-typed instead of relying on parsing strings at runtime.

-Joe
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Thanks for the feedback everyone! We have pushed a changed a bit ago to the proposal reflecting these desires.

Proposal for Keypath by michael-lehew · Pull Request #644 · apple/swift-evolution · GitHub

Quoting from the proposal:

luke[keyPath: keypath(.friends[0].name)]

Really? I can understand marking one or the other, but both, even when there's no ambiguity?

Let's pretend we're the type checker here. The `luke[keyPath: _]` part will create a context where we know we have an `AnyKeyPath`, `PartialKeyPath<Person>`, `KeyPath<Person, U>`, or `WritableKeyPath<Person, U>`. So if the core team's concern is about namespace clashes between `Person`'s static members and key paths, why not hang the key paths off the various `KeyPath` types? That is, this:

  struct Person {
    var friends: [Person]
    var name: String
  }

Implies this:

  extension PartialKeyPath where Root == Person {
    static let friends: WritableKeyPath<Person, [Person]>
    static let name: WritableKeyPath<Person, String>
  }

And this:

  keypath(Person, .friends[0].name)

Desugars to this:

  PartialKeyPath<Person>.friends[0].name

So in a context where you already know you're passing a key path, you can simply write this:

  luke[keyPath: .friends[0].name]

Which applies normal "unresolved member" logic to look it up in `PartialKeyPath`.

The result would be that you would have to explicitly, syntactically mark key paths except when the context already implied you were looking for one. In an unconstrained generic context, you would not get a key path without using `#keyPath` or explicitly naming a key path type. You would only need to worry about clashes if a call was overloaded to accept *both* `T` and `PartialKeyPath<T>`; if we found that possibility troubling, we could penalize unresolved member lookups that resolve to key paths, so type inference would favor static members over key paths even in those cases.

Would that work for people?

···

On Mar 29, 2017, at 4:13 PM, Michael J LeHew Jr via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

Referencing Key Paths

Forming a KeyPath borrows from the same syntax added in Swift 3 to confirm the existence of a given key path, only now producing concrete values instead of Strings. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the appending methods on KeyPath.

There is no change or interaction with the keypath() syntax introduced in Swift 3. keypath(Person.bestFriend.name) will still produce a String, whereas keypath(Person, .bestFriend.name) will produce a KeyPath<Person, String>.

This distinction seems arbitrary and confusing. The user is supposed tor remember that the keypath(Person.bestFriend.name) form produces a string while the keypath(Person, .bestFriend.name) form produces a key path object? I don’t think we’re advancing here. What would be the effect if just the former was valid, and (always/now) produced a keypath object that was convertible to string? How bad would the breakage be?

James

···

On Mar 29, 2017, at 4:13 PM, Michael J LeHew Jr via swift-evolution <swift-evolution@swift.org> wrote:

Thanks for the feedback everyone! We have pushed a changed a bit ago to the proposal reflecting these desires.

Proposal for Keypath by michael-lehew · Pull Request #644 · apple/swift-evolution · GitHub

-Michael

On Mar 29, 2017, at 2:49 PM, Douglas Gregor <dgregor@apple.com <mailto:dgregor@apple.com>> wrote:

On Mar 17, 2017, at 10:04 AM, Michael LeHew via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the following proposal:

The Swift core team discussed this proposal draft and had a little bit of pre-review feedback.

Access and Mutation Through KeyPaths
To get or set values for a given root and key path we effectively add the following subscripts to all Swift types.

Swift
extension Any {
    subscript(path: AnyKeyPath) -> Any? { get }
    subscript<Root: Self>(path: PartialKeyPath<Root>) -> Any { get }
    subscript<Root: Self, Value>(path: KeyPath<Root, Value>) -> Value { get }
    subscript<Root: Self, Value>(path: WritableKeyPath<Root, Value>) -> Value { set, get }
}

Swift doesn’t currently have the ability to extend Any, so this is (currently) pseudocode for compiler magic that one day we might be able to place. Additionally, the “Root: Self” constraint isn’t something we support in the generics system. A small note indicating that this is pseudo-code meant to get the point across (rather than real code to drop into the standard library/Foundation) would be appreciated.

More importantly, this adds an unlabeled subscript to every type, which raises concerns about introducing ambiguities—even if not hard ambiguities that prevent code from compiling (e.g., from a Dictionary<AnyKeyPath, …>)---they can still show up in code completion, diagnostics, etc.

The core team would prefer that this subscript distinguish itself more, e.g., by labeling the first parameter “keyPath” (or some better name, if there is one). Syntactically, that would look like:

  person[keyPath: theKeyPathIHave]

Referencing Key Paths

Forming a KeyPath borrows from the same syntax used to reference methods and initializers,Type.instanceMethod only now working for properties and collections. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the appending methods on KeyPath.

The core team was concerned about the use of the Type.instanceProperty syntax for a few reasons:

  * It doesn’t work for forming keypaths to class/static properties (or is ambiguous with the existing meaning(, so we would need another syntax to deal with that case
  * It’s quite subtle, even more so that the existing Type.instanceMethod syntax for currying instance methods

There is no change or interaction with the keypath() syntax introduced in Swift 3.

The core team felt that extending the keypath syntax was a better syntactic direction to produce key-paths.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Thanks for the feedback everyone! We have pushed a changed a bit ago to the proposal reflecting these desires.

Proposal for Keypath by michael-lehew · Pull Request #644 · apple/swift-evolution · GitHub

-Michael

I'm not a fan of the new syntax for creating key paths. To me, it feels like they've been demoted to second class citizens of the language simply because of how more verbose it now is. The new syntax is also too confusingly similar to string key paths: I had to look closely at the code to see the difference. Is there no symbol we can use to make it ambiguous? Ideas:

Person::friend.lastName
Person/friend.lastName
Person#friend.lastName

I'm a fan of the first one as it has similarities to names pacing in C++.

David.

···

Sent from my iPhone

On 30 Mar 2017, at 01:13, Michael J LeHew Jr via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 29, 2017, at 2:49 PM, Douglas Gregor <dgregor@apple.com> wrote:

On Mar 17, 2017, at 10:04 AM, Michael LeHew via swift-evolution <swift-evolution@swift.org> wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the following proposal:

The Swift core team discussed this proposal draft and had a little bit of pre-review feedback.

Access and Mutation Through KeyPaths
To get or set values for a given root and key path we effectively add the following subscripts to all Swift types.

Swift
extension Any {
    subscript(path: AnyKeyPath) -> Any? { get }
    subscript<Root: Self>(path: PartialKeyPath<Root>) -> Any { get }
    subscript<Root: Self, Value>(path: KeyPath<Root, Value>) -> Value { get }
    subscript<Root: Self, Value>(path: WritableKeyPath<Root, Value>) -> Value { set, get }
}

Swift doesn’t currently have the ability to extend Any, so this is (currently) pseudocode for compiler magic that one day we might be able to place. Additionally, the “Root: Self” constraint isn’t something we support in the generics system. A small note indicating that this is pseudo-code meant to get the point across (rather than real code to drop into the standard library/Foundation) would be appreciated.

More importantly, this adds an unlabeled subscript to every type, which raises concerns about introducing ambiguities—even if not hard ambiguities that prevent code from compiling (e.g., from a Dictionary<AnyKeyPath, …>)---they can still show up in code completion, diagnostics, etc.

The core team would prefer that this subscript distinguish itself more, e.g., by labeling the first parameter “keyPath” (or some better name, if there is one). Syntactically, that would look like:

  person[keyPath: theKeyPathIHave]

Referencing Key Paths

Forming a KeyPath borrows from the same syntax used to reference methods and initializers,Type.instanceMethod only now working for properties and collections. Optionals are handled via optional-chaining. Multiply dotted expressions are allowed as well, and work just as if they were composed via the appending methods on KeyPath.

The core team was concerned about the use of the Type.instanceProperty syntax for a few reasons:

  * It doesn’t work for forming keypaths to class/static properties (or is ambiguous with the existing meaning(, so we would need another syntax to deal with that case
  * It’s quite subtle, even more so that the existing Type.instanceMethod syntax for currying instance methods

There is no change or interaction with the keypath() syntax introduced in Swift 3.

The core team felt that extending the keypath syntax was a better syntactic direction to produce key-paths.

  - Doug

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

How often do you have a property with the exact same name and type on both the instance and type? When you *do* have one, how often would it be better off with a name like `defaultFoo` instead of plain `foo`?

Why is this a problem for keypaths, but not for unbound methods?

How is this different from a hundred other places in Swift where we allow overloading and tolerate ambiguity in order to enjoy nicer syntax?

When, in practice, do you expect this to cause trouble?

···

On Mar 19, 2017, at 12:57 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

I’ve got to agree with this. With the proposed syntax, it’s unclear whether you’re referring to a static property or a key path. It’s going to cause confusion. There needs to be some kind of syntactic way to differentiate the two.

--
Brent Royal-Gordon
Architechies

I’ve got to agree with this. With the proposed syntax, it’s unclear whether you’re referring to a static property or a key path. It’s going to cause confusion. There needs to be some kind of syntactic way to differentiate the two.

Charles

···

On Mar 19, 2017, at 2:51 PM, Jean-Daniel via swift-evolution <swift-evolution@swift.org> wrote:

Le 19 mars 2017 à 16:41, Ricardo Parada via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

I agree that they can get mixed up with static properties. However, I think I would not mind because it would only cause an ambiguity when having a static property with the same name as the property which would be an odd practice I think.

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

I’ve got to agree with this. With the proposed syntax, it’s unclear whether you’re referring to a static property or a key path. It’s going to cause confusion. There needs to be some kind of syntactic way to differentiate the two.

How often do you have a property with the exact same name and type on both the instance and type? When you *do* have one, how often would it be better off with a name like `defaultFoo` instead of plain `foo`?

Why is this a problem for keypaths, but not for unbound methods?

How is this different from a hundred other places in Swift where we allow overloading and tolerate ambiguity in order to enjoy nicer syntax?

When, in practice, do you expect this to cause trouble?

+1

···

Sent from my iPhone

On Mar 19, 2017, at 3:45 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 19, 2017, at 12:57 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Even if there *isn’t* a property with the same name, it’s still confusing, because to a reader unfamiliar with the code, it’s not clear what you’re looking at.

Unbound methods are annoying too. At least with them, though, there are *usually* naming conventions that differentiate the two from each other (but not always. Quick, between FileManager, NSParagraphStyle, IOBluetoothHostController, NSTimeZone, and and NSUserNotificationCenter, which ones require you to put parens after the ‘default’ accessor, and which don’t?).

Charles

···

On Mar 19, 2017, at 3:45 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

On Mar 19, 2017, at 12:57 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

I’ve got to agree with this. With the proposed syntax, it’s unclear whether you’re referring to a static property or a key path. It’s going to cause confusion. There needs to be some kind of syntactic way to differentiate the two.

How often do you have a property with the exact same name and type on both the instance and type? When you *do* have one, how often would it be better off with a name like `defaultFoo` instead of plain `foo`?

Why is this a problem for keypaths, but not for unbound methods?

How is this different from a hundred other places in Swift where we allow overloading and tolerate ambiguity in order to enjoy nicer syntax?

When, in practice, do you expect this to cause trouble?

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

I’ve got to agree with this. With the proposed syntax, it’s unclear whether you’re referring to a static property or a key path. It’s going to cause confusion. There needs to be some kind of syntactic way to differentiate the two.

How often do you have a property with the exact same name and type on both the instance and type? When you *do* have one, how often would it be better off with a name like `defaultFoo` instead of plain `foo`?

Why is this a problem for keypaths, but not for unbound methods?

How is this different from a hundred other places in Swift where we allow overloading and tolerate ambiguity in order to enjoy nicer syntax?

When, in practice, do you expect this to cause trouble?

Even if there *isn’t* a property with the same name, it’s still confusing, because to a reader unfamiliar with the code, it’s not clear what you’re looking at.

This is true of many things. It is why IDEs make type information readily available.

···

Sent from my iPhone

On Mar 19, 2017, at 4:02 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 19, 2017, at 3:45 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

On Mar 19, 2017, at 12:57 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

Unbound methods are annoying too. At least with them, though, there are *usually* naming conventions that differentiate the two from each other (but not always. Quick, between FileManager, NSParagraphStyle, IOBluetoothHostController, NSTimeZone, and and NSUserNotificationCenter, which ones require you to put parens after the ‘default’ accessor, and which don’t?).

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I was thinking that maybe it could be an error or warning to have a static property with the same name as a normal property. But I'm simply thinking out loud a possible solution to this ambiguity problem without introducing a symbol for key paths. Just trying to see if we can keep it simple.

I'm not arguing in favor of this though. Im just trying to bring up possible solutions so we that we can better compare the alternatives and understand the pros and cons of each.

By the way I do like the colon notation that you and others have suggested. I hope we pick the best at the end.

I'm excited about this proposal and want it to be the successful. One of the most exciting things happening in swift right now IMHO.

···

On Mar 19, 2017, at 3:51 PM, Jean-Daniel <mailing@xenonium.com> wrote:

Le 19 mars 2017 à 16:41, Ricardo Parada via swift-evolution <swift-evolution@swift.org> a écrit :

I agree that they can get mixed up with static properties. However, I think I would not mind because it would only cause an ambiguity when having a static property with the same name as the property which would be an odd practice I think.

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

Without context, the following statement is ambiguous, This ambiguity can easily be avoid by choosing an other syntax. So why would we want to introduce it in the first place ?

let name = Person:friends[0].name

I was defining static properties with the same name as the property in order to smart key paths. For example:

Class Person {
   static firstName: KeyPath<String>(“firstName”)
   static lastName: KeyPath<String>(“lastName”)

   var firstName: String
   var lastName: String

   …
}

So that I could create qualifiers, ie. Person.firstName.like(“R*”), and sort orderings, Person.firstName.asc().

I think with these smart key paths the need for these static properties that mirror the property on the instance would not be required.

On Mar 17, 2017, at 2:27 PM, Vladimir.S via swift-evolution <swift-evolution@swift.org> wrote:

On 17.03.2017 20:04, Michael LeHew via swift-evolution wrote:

Hi friendly swift-evolution folks,

The Foundation and Swift team would like for you to consider the following
proposal:

Many thanks,
-Michael

Smart KeyPaths: Better Key-Value Coding for Swift
...

Just my 2 cents. FWIW, I feel like the proposed syntax is VERY confusing.

Person.friends[0].name - like access to static property. T.method can't have subscript for it and then some field. So, I believe such key paths deserve its own syntax.

luke[path] - very close to access the subscript of instance. In compare to T.method, we can call result of T.method(i.e. let m = T.method; m()), just like the T.method itself(foo.method()). But here we are calling some kind of subscript instead of access property by "path".

There is no some 'special' marker for me that saying "hey, there is references to properties used" from the first look.

Probably we can consider another separators, like

Person:friends[0].name
luke:path

Person->friends[0].name
luke->path

Person[friends[0].name] // single brackets for type
luke[[path]] // double brackets for instance

Person@friends[0].name
luke@path

or other with help of community.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Sent from my iPhone

I disagree. How the reader is supposed to now there is a static property or not ? Having readable code is more important than having easy to write code.

I’ve got to agree with this. With the proposed syntax, it’s unclear whether you’re referring to a static property or a key path. It’s going to cause confusion. There needs to be some kind of syntactic way to differentiate the two.

How often do you have a property with the exact same name and type on both the instance and type? When you *do* have one, how often would it be better off with a name like `defaultFoo` instead of plain `foo`?

That crossed my mind too. If we have a static property with the same name as a property then make it a warning and we can easily rename one or the other in our code to fix the warning.

Why is this a problem for keypaths, but not for unbound methods?

Hi Brent, what is an unbound method? Do you have a simple example just so that I can better follow what you are saying? Thanks.

···

Sent from my iPhone

On Mar 19, 2017, at 4:53 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 19, 2017, at 3:45 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

On Mar 19, 2017, at 12:57 PM, Charles Srstka via swift-evolution <swift-evolution@swift.org> wrote:

How is this different from a hundred other places in Swift where we allow overloading and tolerate ambiguity in order to enjoy nicer syntax?

When, in practice, do you expect this to cause trouble?

+1

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution