Ambiguous Method Overload with Closures in Swift, But Only When Closure Returns a Value

Ambiguous method overload with closures in Swift, but only when closure returns a value

You are misunderstanding what's happening. It's not about returning a value. Simplify, for a clearer demonstration of that:

func call8(_: UInt8) -> String { "8" }
func call16(_: UInt16) -> String { "16" }

func ƒ(_ call: (UInt8) -> String) { call(8) }
func ƒ(_ call: (UInt16) -> String) { call(16) }

ƒ(call8) // "8"
ƒ(call16) // "16"

Instead, it's all about multiple lines. Read all about it!

https://forums.swift.org/t/multiline-closure-is-not-inferring-the-return-type/40013

As for the fix, you've got to explicitly type, for disambiguation. /p>

(string, arg: UInt8)
(string, arg: UInt16)

Is there shorthand to specify a specific overload when passing a function as a closure-based argument?

In Swift, the "base name" of a method is the method name without any arguments:

  • Foo.init(x:y:z:)Foo.init
  • Foo.bar(_:)Foo.bar
  • Foo.baz(baz:)Foo.baz
  • Foo.quux()Foo.quux

When referring to a method by name (rather than calling it), Swift will allow you to refer to it by its base name, so long as the usage is not ambiguous:

struct Foo {
func f(intValue: Int) {}
}

let f = Foo().f

However, when there are multiple methods with the same base name, you can run into situations with ambiguity:

struct Foo {
func f(intValue: Int) {}
func f(stringValue: String) {}
}

let f = Foo().f // error: ambiguous use of 'f'

When this happens, you can either:

  1. Use explicit typing to help disambiguate the methods, if possible:

    let f1: (Int) -> Void = Foo().f
    let f2: (String) -> Void = Foo().f

    Or,

  2. Refer to the method by its fully-qualified name (name with parameter names included):

    let f1 = Foo().f(intValue:)
    let f2 = Foo().f(stringValue:)

In your case, because both methods have the same type signature, you can't use approach (1) to disambiguate between the calls, and will have to resort to (2).

The issue you ran into is that the parameter name you were using was slightly off:

// ❌ Foo.init(doubleValue:)
// ✅ Foo.init(valueToDouble:)

let foos = values.map(Foo.init(valueToDouble:)) // ✅

This does work, and will work as a shorthand instead of having to call the method directly inside of a closure argument.

Ambiguous use of operator ''

The closure must explicitly declare the type of the s1 and s2 parameters and that type must implement > operator. The typical way to do that is to make the signature of that closure ensure that the two parameters are (a) the same type; and (b) conform to the Comparable protocol.

If you want simple2 to take any Comparable type, rather than a closure, you could define a generic function:

func simple2<T: Comparable>(_ s1: T, _ s2: T) -> Bool {
return s1 > s2
}

Then you could call it with any Comparable type.

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.

Swift inferred closure parameter puzzle

It is ambiguous. Both .times() methods can be used with the given closure expression if the parameter type of the closure is not known.

If you just write { i in println("Year \(i)") }, it is just a closure that takes one parameter of any type. Well, EVERY function type in Swift can be viewed as taking one parameter:

  • What you think of as zero-parameter functions actually take one parameter of type () (a.k.a. Void), of value (), that's why the type is written as () -> something
  • What you think of as multiple-parameter functions actually take one parameter of tuple type, a tuple of all the "multiple arguments", that's why the type is written as (foo, bar) -> something.

So, basically, your closure expression, without specifying the type of i, can be inferred to ANY function type in Swift that returns Void. The functions taken by both .times() methods match this -- for the first .times() method, it is inferred as a function of type () -> (), i.e. i has type (); for the second .times() method, it is inferred as a function of type Int -> (), i.e. i has type Int.



Related Topics



Leave a reply



Submit