How to specify which is the default function when functions use the same name, like Array.reversed()

When you use Array.reversed(), Swift is by default using the ReversedCollection version, unless you define a return value type, like:

let array1 = [String]().reversed() // array1: ReversedCollection<[String]>
let array2: [String] = [String]().reversed()

But if I try the same thing, it won't be allowed:

func test() -> Bool { false }
func test() -> Int { 1 }

test() // error: Ambiguous use of 'test()'

Is that any way to specify which is the default function?

Usually functions with return types are used to have the result being assigned to a variable, so there shouldn't be cases in which you can't specify the resulting type (Bool or Int in this case). You can however cast the result of the function to achieve your goal:

func test() -> Bool { print("Bool"); return false }
func test() -> Int { print("Int"); return 1 }

test() as Bool // Prints "Bool"
test() as Int  // Prints "Int"

I know your point, but in some cases, I did need to have the same name function(maybe without return type) in extensions like:

struct typeA<T> { }
extension typeA where T: Equatable {
    func doSomething() {
    
    }
}
extension typeA where T: Hashable {
    func doSomething() {
        // if T is Hashable, I have to deal with other stuff.
    }
}

And I want the "T is Hashable" version of doSomething() to be the default function

@_disfavoredOverload
func test() -> Bool { false }

func test() -> Int { 1 }


print(test())    // prints 1
2 Likes

The underscore in @_disfavoredOverload means it’s not really ready for public use. An easier way in this case (if more indirect) would be to add a dummy argument with a default value, since functions with default arguments are considered “less specific” than those with the exact right number.

EDIT: And in your Hashable vs. Equatable case, you shouldn’t have to do anything, since Hashable is already more specific than Equatable.

3 Likes

For concrete instances this is correct, but in generic code the version called depends on the static constraints not the actual conformances of the type:

struct TypeA<T> { var value: T }

extension TypeA where T: Equatable {
  func doSomething() { print("T is Equatable") }
}

extension TypeA where T: Hashable {
  func doSomething() { print("T is Hashable") }
}

func doSomethingIfValuesEqual<T: Equatable>(_ a: TypeA<T>, _ b: TypeA<T>) {
  if a.value == b.value { a.doSomething() }
}

Usage:

let x = TypeA<Int>(value: 1)
x.doSomething()                 // "T is Hashable"
doSomethingIfValuesEqual(x, x)  // "T is Equatable"

Fair point. I didn't mention that since there's not currently any way to get the other behavior in that case; the only methods chosen dynamically in Swift are protocol requirements and overridable methods on classes.