Method references with passed arguments?

I often would like to hold a method reference to call at a later time, with the arguments supplied.

Pseudocode, using \ to distinguish between calling the method and getting a reference to the method:

func add(a: Int, b: Int) -> Int {
  return a + b
}

let unappliedAddReference: ((Int, Int) -> Int).Type = \add(a: 2, b: 3)

let result: Int = unappliedAddReference()  // 5

This is useful as a replacement for the builder pattern or lazy instantiation of something expensive:

public class ViewController {
     init(title: String data: Data)
}

let builder = \ViewController(title: "Hello World", data: data)
let ViewController = builder()

Does this exist somewhere? If not, this seems like a great language addition that could fit well with KeyPath syntax.

Can't you use a closure for that?

2 Likes

I can do this with closures:

func add(a: Int, b: Int) -> Int {
    return a + b
}

let lazyAdd: (Int, Int) -> (() -> Int) = { (a: Int, b: Int) in
    return {
        add(a: a, b: b)
    }
}

let unappliedAddReference = lazyAdd(2, 3)

let result = unappliedAddReference() // 5

But you'll notice that the lazy method signature has to drop the named parameters, because function types cannot have argument labels. So we've lost the original method signature and it's also now quite difficult to read the lazyAdd method.

1 Like

Why define lazyAdd at all? I don't think it adds much readability...

func add(a: Int, b: Int) -> Int {
  return a + b
}

let unappliedAddReference = { add(a: 2, b: 3) }

let result = unappliedAddReference()  // 5
2 Likes

Swift used to have a defined curry syntax that would let you write:

func myCurriedFunc(_ y: Int)(_ x: Int)(_ z: Int) -> Int {
  return y + x + z
}

let firstApplication = myCurriedFunc(2)
let secondApplication = firstApplication(3)
let result = secondApplication(3)

But this was removed in Swift 3? much to my chagrin. I used this quite a bit and I thought it was a fairly expressive feature. The issue I believe was that it caused a lot of complexity in the compiler. But I would love to see this type of feature again.

I suggest you read the proposal that removed it, and the alternatives considered. It brings up the idea of adding Scala like partial application syntax (which I love) and would be a massive +1 if this is put on the table.

2 Likes

Another reason might be that creating a standalone builder for a method with default parameters isn't possible, due to default argument not permitted in a tuple type. The following requires dropping the original default parameters to compile:

class ListView {
    init(title: String, maxResults: Int = 10, paginated: Bool = false) {}
}

let builder: (String, Int, Bool) -> (() -> ListView) = { (title: String, maxResults: Int, paginated: Bool) in
    return {
        ListView(title: title, maxResults: maxResults, paginated: paginated)
    }
}

This is what I had in mind, @ebg. I don't know what argument labels have to do with it. You can see the argument labels in the closure that @DeFrenZ wrote. The call to the closure doesn't have labels because it doesn't have arguments. So this really does seem to be exactly what you wanted, just with a different syntax.