@dynamicMemberLookup
Discarding of argument labels makes this really not work with @dynamicMemberLookup
as Swift APIs quite often differentiate just by argument labels, making those APIs ambiguous to call.
It would make it a source breaking change to introduce new method overloads that are just disambiguated by argument labels, essentially on any Type. It also would fail to disambiguate method and properties, making just this proposal source breaking even without further changes.
In fact, the @dynamicMemberLookup
integration actually has the same problems as if we would allow KeyPath method references without argument labels as was discussed previously.
Even the example in the proposal shouldn't compile because subtract
is ambiguous:
struct Calculator {
var subtract: (Int, Int) -> Int { return { $0 + $1 } }
func subtract(this: Int) -> Int { this + this}
func subtract(that: Int) -> Int { that + that }
}
let dynamicCalculator = DynamicKeyPathWrapper(root: Calculator())
let subtract = dynamicCalculator.subtract // <- ambiguous, could be the property or one of the two methods.
print(subtract(10))
I think this either needs to support argument names (which I would really like but sounds rather difficult) or remove this integration.
Edit: I have tried it out with the latest nightly compiler.. This example seems to resolve to the property subtract
. It still fails to compile because the call to subtract
now expects two arguments because the subtract
property returns a closure with two arguments. This should at least make this a non-source breaking for existing code.
However, as soon as users start using this new feature, it would still introduce ambiguities if a method with the same base name but different argument labels are introduced. That effectively would make it impossible to add new method overloads based on argument names to any type without risking to introduce source breaking changes for users that use @dynamicMemberLoop
with method key paths. That is very undesirable.
mutating methods
I'm also quite concerned that mutating methods aren't supported, nor mentioned in the futures directions (there isn't even Future Directions section?). I can't really imagine how we could support mutating methods in the future without a very different strategy.
This also seems in direct conflict with the motivation section:
Key path methods and initializers will also enjoy all of the benefits offered by existing key path component kinds, e.g. simplify code by abstracting away details of how these properties/subscripts/methods are modified/accessed/invoked
Emphasis mine.
IMHO "value semantics" is one of greatest Swift feature. It would be quite unfortunate if we wouldn't support value types. As proposed this can only abstract modifications for types that have reference semantics.
consuming methods
It is a bit unclear how deeply consuming
methods are actually supported. Does this actually support consuming
methods without copying self
or does this just convert it to a non consuming method and adds a copy before calling it? Concretely, would this trigger Copy on Write (CoW) or not?:
extension Array
consuming func appending(_ element: Element) -> Self {
self.append(element)
return self
}
}
let array = [0, 1, 2]
// is CoW triggered here?
let newArray = array[keyPath: \.appending(3)]
I think it does need to do a copy as consuming get
accessors aren't yet supported.
Is this something that could be added later? Any other way to not trigger CoW?
With that being said, I very much think this is a problem worth solving.
However, without a future direction nor any examples in the motivation section I don't really see a use case where I could use method key paths as proposed. It doesn't work for my use cases if it doesn't support mutating methods nor argument names with @dynamicMemberLoopkup
.