Hi all,
@dan-zheng and I have been experimenting with a protocol that helps you iterate over all stored properties at runtime.
Here's the idea:
protocol StoredPropertyIterable {
associatedtype AllStoredProperties : Collection
where AllStoredProperties.Element == PartialKeyPath<Self>
static var allStoredProperties: AllStoredProperties { get }
}
By conforming to StoredPropertyIterable
, you get a type property that represents a collection of key paths to all stored properties defined in the conforming type. The conformance is compiler-derived.
struct Foo : StoredPropertyIterable {
var x: Int
var y: String
// Compiler-synthesized:
static let allStoredProperties: [PartialKeyPath<Foo>] {
return [\.x, \.y]
}
}
A protocol extension can provide a recursivelyAllStoredProperties
computed property that returns an array of key paths to stored properties of this type and key paths to any nested stored properties whose parent also conforms to StoredPropertyIterable
. This can also be a lazy collection based on allStoredProperties
, of course.
extension StoredPropertyIterable {
static var recursivelyAllStoredProperties: [PartialKeyPath<Self>]
}
struct Bar : StoredPropertyIterable {
var foo: Foo
var z: Int
var w: String
}
Bar.allStoredProperties
// => [\Bar.foo, \Bar.z, \Bar.w]
Bar.recursivelyAllStoredProperties
// => [\Bar.foo, \Bar.foo.x, \Bar.foo.y, \Bar.z, \Bar.w]
Why do we believe StoredPropertyIterable
should be added to the standard library? It provides a canonical API for accessing all stored properties of a struct, and can define away the existing compiler synthesis for derived Hashable
conformances. Here's @Joe_Groff's earlier idea: keypath-hashable.swift · GitHub.
Advanced use cases lie in many fields, one of which I'm familiar with is machine learning. ML optimizers operate on bags of parameters (sometimes defined as stored properties, sometimes as collections), and apply an update algorithm to every parameter. Since this use case is more complex than stored properties and requires key paths to nested parameters, I won't go into details for now. If you are interested, you can look at CustomKeyPathIterable
in the gist below.
The following gist demonstrates a simple StoredPropertyIterable
and a more advanced protocol called CustomKeyPathIterable
that lets you define key paths to non-compile-time elements. Comments are welcome!