-
Notifications
You must be signed in to change notification settings - Fork 9
Alternative implementation of StaticStructural #10
Alternative implementation of StaticStructural #10
Conversation
CustomEquatable: Point14 (reference) 25.0 ns ± 40.39 % 1000000 | ||
CustomEquatable: Point15 (generic) 1145.0 ns ± 20.39 % 1000000 | ||
CustomEquatable: Point15 (reference) 25.0 ns ± 38.51 % 1000000 | ||
CustomEquatable: Point16 (generic) 1213.0 ns ± 12.65 % 1000000 |
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.
Runtime performance is degraded up to 10x in some cases due to addition of KeyPaths. It's unclear how much effort will it take to bring it back down while preserving KeyPaths in the API.
StructuralCons< | ||
StructuralProperty<Point3>, | ||
StructuralProperty<LabeledPoint3, Point3, Point3>, |
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.
Due to the fact that we don't hide property wrapper types, unlike KeyPaths we need to have 2 type parameters for value: one for wrapped and another for unwrapped value. This has unfortunate an unpleasant complexity cost.
/cc @dabrahams |
public var name: String | ||
public var value: Value | ||
public var isMutable: Bool | ||
public var value: WrappedValue |
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 looks like we can't get name out of KeyPath in a portable manner (there is a macOS-only solution that relies on NSExpression). This is intended because KeyPaths can express arbitrary multi-step field accesses.
What's even more unsettling is that we use StructuralProperty
for both StructuralStruct
properties and StructuralEnum
associated values. Associated values don't work with KeyPaths at all, so if we unify this even further by removing name
, we'll have to add yet another structural representation type StructuralAssociatedValue
which is nearly identical to StructuralProperty
, only with a name
instead of keyPath
.
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.
Nice work here @shabalind ! Thanks for this PR. I think this is really good, and probably better than having a doubling of the API surface. That said, some of the more interactions with KeyPaths (in particular WrappedValue
) are quite unfortunate.
public struct StructuralStruct<Properties> { | ||
public var type: Any.Type? | ||
public struct StructuralStruct<BaseType, Properties> { | ||
public var type: BaseType.Type? |
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.
Why is type
Optional<BaseType.Type>
and not simply BaseType.Type
? (Note: I realize this was the case before your change too.)
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 you look at implementation of Zero
we need to be able to create an instance StructuralStruct
based on known type parameters only.
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.
But don't we now have BaseType
? Couldn't we do something like:
extension StructuralStruct: Zero where Properties: Zero {
public static var zero: Self {
return StructuralStruct(type: BaseType.self, Properties.zero)
}
}
self.type = type | ||
self.properties = properties | ||
} | ||
} | ||
|
||
/// Structural representation of a Swift property. | ||
public struct StructuralProperty<Value> { | ||
public struct StructuralProperty<BaseType, Value, WrappedValue> { |
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.
Please document Value
vs WrappedValue
. (I only understood the distinction when looking through the examples and seeing one using property wrappers.)
Agreed that this is some unfortunate complexity. :-(
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.
Added comment in b20fb72
} | ||
|
||
protocol _CollectKeyPaths { | ||
func collectKeyPaths(into keyPaths: inout [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.
What about:
func collectKeyPaths(into keyPaths: inout [AnyKeyPath]) | |
func collectKeyPaths(into keyPaths: inout [PartialKeyPath<Self>]) |
or:
func collectKeyPaths(into keyPaths: inout [AnyKeyPath]) | |
associatedtype BaseType | |
func collectKeyPaths(into keyPaths: inout [PartialKeyPath<BaseType>]) |
?
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.
This complicated implementation of _CollectKeyPath because we don't have BaseType
for StructuralCons and StructuralEmpty. Adding them just for this use case seems like a massive overkill.
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.
Agreed that adding BaseType
to StructuralCons
doesn't make sense (although it can likely be conditionally inferred with the "BaseTypeProtocol" I've used in related PRs), and StructuralEmpty
of course "should" be Never
which neatly solves that problem.
I say "should" because I'm not sure how I feel about it yet. While it has some aspect of feeling more regular, I'm not sure it substantially simplifies use of SGP in practice. But this is only a weak prior.
name time std iterations | ||
--------------------------------------------------------------------------------- | ||
Additive: Point1 (specialized) 28.0 ns ± 52.30 % 1000000 | ||
Additive: Point1 (generic) 124.0 ns ± 32.32 % 1000000 |
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.
Note: seems like we've fallen off an optimizer performance cliff. :-(
This has been an interesting experiment, but we decided to postpone this for now. We might revisit it later. |
This PR proposes an alternative implementation of StaticStructural from #9:
In contrast to #9 this approach does not require duplication of the API to provide new representation types for static counterparts.