Function Overloading in Swift

Swift: method overloads that only differ in return type

Where is this documented?

As for subscript:

Language Reference / Declarations / Subscript Declaration

You can overload a subscript declaration in the type in which it is declared, as long as the parameters or the return type differ from the one you’re overloading.

Language Guide / Subscripts / Subscript Options

A class or structure can provide as many subscript implementations as it needs, and the appropriate subscript to be used will be inferred based on the types of the value or values that are contained within the subscript braces at the point that the subscript is used.

I cannot find any official docs about overloading methods or functions. but in the Swift Blog:

Redefining Everything with the Swift REPL / Redefinition or Overload?

Keep in mind that Swift allows function overloading even when two signatures differ only in their return type.

Overloading generic functions in iOS Swift

You haven't quite focussed on the actual issue. Let's eliminate everything irrelevant from the example. This compiles and works as expected:

struct TestFinder {

func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}

func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}

func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}

}

And here we'll test it:

    func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f) // "four"

But if you add the version with one passed function parameter, everything breaks down:

struct TestFinder {

func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}

func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}

func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}

func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}

Now we can't compile, because the first version is seen as a candidate. And indeed, if we remove the other versions, we still compile!

struct TestFinder {

func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}

}

That's the weird part. We still compile, even though we are saying:

    func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f)

Evidently, this function with four parameters is seen by the compiler as "fitting" the declaration with just one generic parameter.

I regard this as a bug. I think I can guess what might cause it; it could have to do with the legacy of function parameter list as tuples. This function f is "equivalent" to a function taking a single parameter consisting of a four-string tuple. Nevertheless, you cannot actually call the function inside doSomething with a four-string tuple; I cannot find a way to call it at all.

So, I would say, regard this as a bug, and work around it for now by removing the first version of your generic.


UPDATE: On the advice of the Swift team, I tested with the May 4, 2020 Swift 5.3 Development toolchain. With it, your code compiles and behaves as expected. This was indeed a bug, and it was fixed as part of

https://bugs.swift.org/browse/SR-8563

Returning for a moment to my version, my code, too, compiles and behaves as expected, with all four versions of doSomething present. However, note that if you delete all but the first version of doSomething, it still compiles and runs. Moreover, you can call function with four parameters by bundling them into a tuple and force casting, like this:

struct TestFinder2 {

func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
function(("manny", "moe", "jack", "henry") as! T)
}

}

That seems to confirm my guess that what you're seeing is a consequence of the hidden tuple-nature of a function's parameter list. One can draw the same conclusion from the discussion of the bug, which refers to "tuple-splatting".

function overloading in swift

It seems you may be running into the same problem mentioned in this reddit thread. It looks like the solution may just be to rename your functions, due to a clash with inherited with Objective C methods.

e.g.

func myPerformOperation(operation: Double -> Double){

func myPerformOperation(operation:(Double, Double)->Double){

(These aren't ideal names—they should be more descriptive—but you get the idea.)

Nested function overloading in Swift

IMHO this is clearer if you consider this variation:

func foo() {

let bar: (param: Int) -> () = { p in
// do stuff
}

func bar(param: Double) {
// do stuff
}

// call functions here
}

With your first bar function definition you are just declaring a constant of type (param: Int) -> ().
Thus with your second declaration you are declaring another constant of different type (param: Double) -> () but having the same name as the already declared bar.

In short it's just like you wrote:

let bar: Int = 0
let bar: Double = 0.0

In which case compiler will complain as well.

Hope this helps.

Precedence of Function Overloading Parameters in Swift

The keyword here is "specific". Swift always finds the most specific overload to call.

Swift first infers that value1 is of type Int, so type(of: value1) is of type Int.Type.

Now we've got a Int.Type as the parameter, Swift looks for an overload that accepts one parameter whose type is exactly Int.Type. It finds one and calls that. The one that accepts a Fooable.Type is not called because it can already call a more specific one that accepts exactly a Int.Type. Here, "specific" means lower down the type hierarchy. Int is considered more specific than Fooable because Int conforms to Fooable.

value3 is inferred to be of type Bool.Type. There is not overload that accepts a Bool.Type, so Swift has to look up the type hierarchy and finds the one that accepts a Fooable.Type. This is the most specific type that you can pass a Bool.Type into.

Swift and params method default values

Your class A has two functions, c(d:), and c(d:e:). In Swift, two functions can share the same “first name”, but be distinguished by their arguments. Hence, the “full name” of a function consists of its name and all of its parameter labels.

Replace the line

aaa.c()

with

aaa.c(d:e:)()

which calls the function c(d:e:) by its full name, and (2) will execute.

Note that aaa.c() is equivalent to aaa.c(d:)(). Swift appears to default to the function with the fewest parameters when the call is ambiguous; this may be the subject of a future Swift Evolution proposal, if it is not already.

How to call an overloaded function when you only have a variable conforming to a lesser type?

There are a couple of ways to go about this...

Use a switch to reestablish the type

Swift needs to know at compile time which overloaded function it is calling. This can't happen if Swift doesn't know at compile time which type of the variable it has.

To get the type information back, you can use a switch to reestablish the type:

func performCommand(_ command:Command) {
guard let todoCommand = command as? ToDoCommand else {
return
}

// Perform some tasks that are common to all ToDoCommands...

switch todoCommand {
case let command as AddToDoCommand:
performCommand(command)
case let command as EditToDoCommand:
performCommand(command)
default: break
}
}

Use polymorphism

A way to let Swift decide which performToDoCommand() command to run at runtime is to use polymorphism.

Add the requirement to implement func performToDoCommand() to the ToDoCommand protocol, and then implement that for each struct that conforms to ToDoCommand. Calling the right one is then simple...

protocol Command {}
protocol ToDoCommand : Command {
func performToDoCommand()
}
protocol UserCommand : Command {}

struct AddToDoCommand : ToDoCommand {
func performToDoCommand() {
print("Add ToDo")
}
}

struct EditToDoCommand : ToDoCommand {
func performToDoCommand() {
print("Edit ToDo")
}
}

struct AddUserCommand : UserCommand {}
struct EditUserCommand : UserCommand {}

class ToDoManager {
func performCommand(_ command:Command) {
guard let todoCommand = command as? ToDoCommand else {
return
}

todoCommand.performToDoCommand()
}
}


Related Topics



Leave a reply



Submit