Introduction
In SE-0042 @Joe_Groff introduced the idea of flattening unapplied method references (UMRs), from the curried form to a function with an additional Self
parameter (the details can be found in the proposal - it's a quick and easy read). This proposal was accepted, yet never implemented.
A benefit resulting from this change would be a massive improvement when passing around methods as function parameters. As Joe Groff himself puts it:
For instance, though you can pass the global
+
operator readily toreduce
to sum a sequence of numbers:
func sumOfInts(ints: [Int]) -> Int {
return ints.reduce(0, combine: +)
}
you can't do the same with a binary method, such as
Set.union
:
func unionOfSets<T>(sets: [Set<T>]) -> Set<T> {
// Error: `combine` expects (Set<T>, Set<T>) -> Set<T>, but
// `Set.union` has type (Set<T>) -> (Set<T>) -> Set<T>
return sets.reduce([], combine: Set.union)
}
Changing UMRs to be flat would fix this problem, as Set.union
would then produce a function of type (Set<T>, Set<T>) -> Set<T>
. So if you've ever written a closure that contains exactly one statement which is just the call to a method... this would not be necessary anymore.
The Problem
As @Slava_Pestov has pointed out in a forum post, changing the curried form to a flattened form would require quite a large amount of work, as the compiler depends on the curried form. Also, there would have to be some sort of backwards compatibility mechanism to avoid breaking source compatibility.
So it would appear that implementing the change as proposed in SE-0042, would not yield a great return on investment anymore.
Just to add a personal opinion, I also find the curried function to be quite a natural result from an UMR. It basically conveys "I want to have a reference to this method, without having chosen upon which instance of Self
it acts."
Consequently, passing a specific instance of Self
to this "unbound" method enacts the step of conveying "I have chosen the instance upon which the method should act."
The Solution?
According to the aforementioned forum post, Joe Groff has put forth the idea of using key path syntax. You would write \MyType.method
to form an uncurried reference.
This would solve the two problems:
- (I'm assuming) No massive compiler changes would be required.
- Source compatibility would not be an issue, as this would be an additive change.
This syntax would also seem to integrate well into the current concept of key paths. Key paths can be used to reference properties in a way that was not possible before, to allow for easier access. Flattened UMRs via key paths would do the same, just for methods.
On the other hand it might be unexpected to users that a key path expression would then produce a function type, instead of the usual KeyPath
.
As @MutatingFunk mentioned, there is also the problem that mutating methods lead to undefined behavior, when using the current UMRs. Which entails that simply adding a new syntax for the flattened version would not be enough. The current UMRs also need to be fixed.
This post is just a first step to gauge interest in such a feature. If you see any complications that may arise from this proposal, or have a better idea on how to realize this feature, please share your thoughts in the comments.
As an average Swift user I would also very much welcome any knowledge on what would actually be required to implement this kind of feature.
Thanks,
Marcus