Diego Freniche
Diego Freniche

Reputation: 5414

Using a class' method as default parameter in Swift

I want to create a class' method that takes some parameters and a function. And I want that function parameter to have a default value. So I'm doing:

class Calculator {

    func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int)? = add) -> Int {
        return (f ?? add)(n1, n2)
    }

    func add(n1: Int, n2: Int) -> Int {
        return n1 + n2
    }
}

Here, f is a function parameter that can be passed (or not, as it is optional). I want add to be the default value for f, so if you do:

Calculator().operate(n1: 10, n2: 20)
// not passing f causes add to be called

Problem: this does not compile, it's telling me that the correct type for f should be: (Calculator) -> (Int, Int) -> Int. That is: add is a method of class Calculator, which makes perfect sense.

But is there a way to express that method as a function?

I can define a external function like:

func externalAdd(n1: Int, n2: Int) -> Int {
    return n1 + n2
}

Then everything works as expected is I change the default value of f:

class Calculator {

    func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int)? = externalAdd) -> Int {
        return (f ?? add)(n1, n2)
    }

    func add(n1: Int, n2: Int) -> Int {
        return n1 + n2
    }
}

Ideas? Is this possible?

Upvotes: 2

Views: 485

Answers (2)

Cédric Moreaux
Cédric Moreaux

Reputation: 146

If you try this:

class Calculator {
  var addFunction: ((Int, Int) -> Int) {
    return add
  }

  func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int)? = addFunction) -> Int {
    func add(n1: Int, n2: Int) -> Int {
      return n1 + n2
    }

    return (f ?? add)(n1, n2)
  }

  func add(n1: Int, n2: Int) -> Int {
    return n1 + n2
  }
}

The compiler error will be "Cannot use instance member 'addFunction' as a default parameter".

The reason is that in swift you cannot use instance variables in function declarations.

Hope it helps

Upvotes: 0

Martin R
Martin R

Reputation: 539965

In

func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int)? = add) -> Int 

add is evaluated as Calculator.add which has the type

 (Calculator) -> (Int, Int) -> Int

as explained in Instance Methods are “Curried” Functions in Swift.

One option is to use nil as the default value (since you do optional chaining anyway later):

class Calculator {

    func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int)? = nil) -> Int {
        return (f ?? add)(n1, n2)
    }

    func add(n1: Int, n2: Int) -> Int {
        return n1 + n2
    }
}

Another option is to make the default function a static method, and the function parameter non-optional:

class Calculator {

    func operate(n1: Int, n2: Int, _ f: ((Int, Int) -> Int) = add) -> Int {
        return f(n1, n2)
    }

    static func add(n1: Int, n2: Int) -> Int {
        return n1 + n2
    }
}

Upvotes: 2

Related Questions