Make closure properties behave like methods

If you have a method, you can often change that to a closure without changing how the API looks like when using it. For example these are equivalent:

struct Foo1 {
  func bar() -> Int {
    return 0
  }
}
struct Foo2 {
  let bar: () -> Int = {
    return 0
  }
}
print(Foo1().bar())
print(Foo2().bar())

It would be nice if it was possible in every case. To do that we would need to make two changes:

  1. Allow method parameters to be a part of variable name
  2. Allow closure properties to fulfil protocol requirements
Some examples where it could be useful

type erasure

protocol Reticulator {
    associatedtype Spline
    func reticulate() -> Spline
}

struct AnyReticulator: Reticulator {
    init<T: Reticulator>(_ reticulator: T) {
        self.reticulate = reticulator.reticulate
    }
    let reticulate: () -> Any
}

mocking

protocol Database {
    func open()
    func fetch(query: Query) throws -> [Any]
    func close()
}

struct MockDatabase {
    var open: () -> Void = {_ in}
    var fetch(query:): (Query) throws -> [Any] = { _ in return [0] }
    var close: () -> Void = {_ in}
}

func testHandlesThrowsCorrectly() {
    var db = MockDatabase()
    db.fetch(query:) = { _ in throw SomeError() }
    someFunction(db)
}

func testHandlesEmptyResult() {
    var db = MockDatabase()
    db.fetch(query:) = { _ in return [] }
    someFunction(db)
}

Dynamically changing implementation for specific objects, instead of the whole classes

if self.sex == .male && chromosome.id == .x {
    chromosome.mix(with:) = { _ in
        fatalError("You shouldn't mix this chromosome")
    }
}

All of that could also be done by having underscored property and a method that just calls that property, but I don't think that's very swifty.

2 Likes

I'm a hard no on this one, at least the way this is currently worded. There are several (not) so subtle differences between anonymous functions (closures in Swift terminology) and methods. Probably the most important is methods get an implicit self parameter while anonymous functions do not. If we start injecting self into anonymous functions that just happen to be properties, it's not an anonymous function anymore, it's a method with a different syntax. We'd also have to change the way anonymous function properties work. IIRC they only get self if the property is lazy.

So is what you're really asking for the ability for anonymous function properties to satisfy protocol method requirements? Because I really don't see the benefit of that vs just using actual methods. The way I look at it it's just a less elegant way of defining a method.

Yes.

I provided few examples of situations where you cannot use actual methods. In these cases you cannot write a normal method, because each instance does something different, even though they are of the same type. You can solve each of these problems in other ways, but not by "just writing a method"

I don't think your examples do a good job of explaining your use case.

Okay I see where you're coming from now I think, and I think this is actually a bad idea. This is bringing in complexity where just implementing the requirement as a method that just calls your closure accomplishes the same thing. Trying to bring this kind of feature in raises a lot more questions about how exactly this would be implemented.

The problem is argument labels. Thereโ€™s a plan to address that though.

See: [Pitch] Allow closures/default params to satisfy protocol requirements

1 Like

This would be the first step, I guess:

Anyone willing to push this forward, feel free to take over the pitch (leave the optional keyword I talk about aside, that's a different story). Compound names + sugar can't happen faster. Its inclusion in Swift 5 would be amazing.

Yeah, being able to give variables of closure type names with argument labels is a long-overdue feature we promised back when we took argument labels out of function types. I'm personally in favor of allowing protocol conformance to be satisfied by anything that fits a general syntactic requirement that x.methodName(...) works. Our current protocol witness matching is too strict.

14 Likes