+1 for requiring self in this case to make capturing self obvious.
We may also support the weak
case. Pseudocode:
// implicit or explicit self
webService.fetchKittenPhotos(completion: weak.displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak.self.displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak.foo.displayKittenPhotos)
// or this? which one would you prefer?
webService.fetchKittenPhotos(completion: weak displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak self.displayKittenPhotos)
webService.fetchKittenPhotos(completion: weak foo.displayKittenPhotos)
as an equivalent of:
webService.fetchKittenPhotos { [weak self] i in
guard let self = self else { return /*see below*/ }
return self.displayKittenPhotos(i)
}
This can work out of the box for closures returning Void
/ Optional
, as otherwise it is not obvious what to return when self == nil. To have it work in all cases we can introduce a DefaultValue
protocol:
protocol DefaultValue {
static var `default`: Self { get }
}
struct Kitten { ... }
struct Puppy: DefaultValue { ... }
...
private func displayKittenPhoto(...) -> Kitten { ... }
private func displayPuppyPhoto(...) -> Puppy { ... }
...
webService.fetchKittenPhoto(completion: weak.displayKittenPhoto)
// Error: "self" can not be captured weakly here as "displayKittenPhoto"
// returns "Kitten" that has no default value. Fixit: Make "Kitten" implementing "DefaultValue" protocol.
webService.fetchPuppyPhoto(completion: weak.displayPuppyPhoto) // ok
.
Somewhat related: IIRC there was a topic recently about supporting "guard let self" syntax in closures and what to do in case of closures returning non void values. With the help of DefaultValue
protocol we can make these two examples equivalent:
webService.fetchKittenPhotos { [weak self] i in
guard let self
displayKittenPhotos(i)
}
webService.fetchKittenPhotos { [weak self] i in
guard let self = self else { return .default }
displayKittenPhotos(i)
}
/* compiler magic
extension Void: DefaultValue {
static var `default`: Self { .init() }
}
*/
// in standard library:
extension Optional: DefaultValue {
static var `default`: Self { nil }
}