-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add KeyPathIterable
protocol and synthesis.
#21557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
public extension KeyPathIterable { | ||
/// An array of all custom key paths of this value and any custom key paths | ||
/// nested within each of what this value's key paths refers to. | ||
var recursivelyAllKeyPaths: [PartialKeyPath<Self>] { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming of recursivelyAllKeyPaths
and the doc comment have room for improvement.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nestedKeyPaths
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestion! I feel nestedKeyPaths
is not an ideal name for this property because it also contains top-level allKeyPaths
. nestedKeyPaths
sounds like it doesn't contain top-level properties (or at least leaves it ambiguous).
We considered nestedKeyPaths
as not just a different name, but a different design option (where allNestedKeyPaths
refers to recursivelyAllKeyPaths
excluding top-level allKeyPaths
, and recursivelyAllKeyPaths
would be defined as allKeyPaths + allNestedKeyPaths
).
We decided not to implement that option though, since we couldn't think of many use cases for "just recursively all nested non-top-level key paths". There seem to be more use cases for "recursively all key paths, including top-level key paths".
The `KeyPathIterable` protocol represents types whose values provide custom key paths to properties or elements. For types that conform to `KeyPathIterable`, the compiler can synthesize a default implementation of `allKeyPaths` based on the type's stored properties. --- `KeyPathIterable` enables generic machine learning optimizers and will be used to replace `ParameterGroup`. The `ParameterGroup` protocol requirements are not sufficiently general for many use cases: it is limited to a single parameter type and does not support joint iteration over parameters for multiple `ParameterGroup` instances. `KeyPathIterable` solves these problems. In addition, it is a generally useful language feature that can model both static stored properties and custom dynamic properties.
5558d7d
to
89b3fe2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
UnresolvedDotExpr(nominalTypeExpr, SourceLoc(), member->getFullName(), | ||
DeclNameLoc(), /*Implicit*/ true); | ||
auto *keyPathExpr = | ||
new (C) KeyPathExpr(SourceLoc(), dotExpr, nullptr, /*Implicit*/ true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Woo! This is so simple.
- Remove `@inlinable` from `KeyPathIterable` extension methods. - Enable `KeyPathIterable` synthesis for empty structs, add test. - Add comment by @rxwei about supporting synthesis for classes in the future.
Mark synthesized `allKeyPaths` declaration with `@inlinable`. Verified that `@inlinable` is correctly added in SILGen: ``` struct Parameters : KeyPathIterable { @sil_stored var w: Float { get set } @sil_stored var b: Float { get set } init(w: Float, b: Float) typealias AllKeyPaths = [PartialKeyPath<Parameters>] @inlinable var allKeyPaths: [PartialKeyPath<Parameters>] { get } } ```
@swift-ci please test tensorflow |
Note: the synthesis condition for This would prevent errors like the following: // error.swift
struct Test : KeyPathIterable {
var x: Float
// Question:
// What if I customize `AllKeyPaths` to be sth that conforms to
// `Collection` but not `ExpressibleByArrayLiteral`?
// And I let the compiler synthesize `allKeyPaths`?
// Answer:
// The compiler doesn't crash, but produces an error because
// the synthesized body of `allKeyPaths` assumes that `AllKeyPaths`
// conforms to `ExpressibleByArrayLiteral`.
typealias AllKeyPaths = UnsafeBufferPointer<PartialKeyPath<Test>>
}
Errors like this aren't unique to |
Great. Thanks for the detailed note! |
|
||
public extension KeyPathIterable { | ||
var _allKeyPathsTypeErased: [AnyKeyPath] { | ||
return allKeyPaths.map { $0 as AnyKeyPath } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since key paths are classes, you can cast the array for these two instead of mapping:
return allKeyPaths as [AnyKeyPath]
(This is really cool functionality!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not really directly convertible. I'm getting this error:
/Users/rxwei/Development/Swift/swift-tf-source/swift/stdlib/public/core/KeyPathIterable.swift:57:24: error: 'Self.AllKeyPaths' is not convertible to '[AnyKeyPath]'; did you mean to use 'as!' to force downcast?
return allKeyPaths as [AnyKeyPath]
~~~~~~~~~~~~^~~~~~~~~~~~~~~
as!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If that works on master, then it's probably because the changes haven't been merged. The tensorflow branch is a pretty old (2018-11-24).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you’re right — I’d missed that allKeyPaths
isn’t an array here, it’s an instance of the associated type. Carry on!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah right. I forgot that too :)
extension Dictionary : KeyPathIterable { | ||
public typealias AllKeyPaths = [PartialKeyPath<Dictionary>] | ||
public var allKeyPaths: [PartialKeyPath<Dictionary>] { | ||
return keys.map { \Dictionary[$0]! } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this (and really all collections) should follow the array example of giving access via indices
, yielding key paths to key-value pairs here. If you make Dictionary.Values
key-path iterable, too, a user could write myDictionary.values.allKeyPaths
to get this result.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Key paths using subscript
and Dictionary.Index
currently cause a crasher on our branch. This may have been fixed on master. We'll make sure to update these once we merge.
The
KeyPathIterable
protocol represents types whose values providecustom key paths to properties or elements.
For types that conform to
KeyPathIterable
, the compiler can synthesizea default implementation of
allKeyPaths
based on the type's storedproperties.
KeyPathIterable
enables generic machine learning optimizers and willbe used to replace
ParameterGroup
.The
ParameterGroup
protocol requirements are not sufficiently generalfor many use cases: it is limited to a single parameter type and does not
support joint iteration over parameters for multiple
ParameterGroup
instances.
KeyPathIterable
solves these problems. In addition, it is a generallyuseful language feature that can model both static stored properties
and custom dynamic properties.
KeyPathIterable.swift
currently lives instdlib/public/core
to enablefuture usage in AutoDiff tests without dependency on TensorFlow module.
KeyPathIterable
is designed by @rxwei and myself.KeyPathIterable
is inspired by:Here is an extended design for
KeyPathIterable
that separates functionality into two protocols:StoredPropertyIterable
andCustomKeyPathIterable
.StoredPropertyIterable
models the static layout of structs and provides a static collection of key paths to all stored properties.CustomKeyPathIterable
can model both static and dynamic structures, including collection types likeArray
andDictionary
.