Even more problems with Swift’s type system: why is Void-returning overload not being preferred here?

i have two overloads called _select(_:where:) shaped like:

extension Sequence 
    func _select<Key>(_ key:Key, where predicate:(Int) throws -> Bool) rethrows 
        -> [Int]
        try self._select(key) { try predicate($0) ? $0 : nil }
    func _select<Key, T>(_ key:Key, where filter:(Int) throws -> T?) rethrows 
        -> [T]
        var selection:[T] = []
        try self._select(key) 
            if let selected:T = try filter($0)
        } as ()
//       ~~~~~^ 
//  why is this needed?
        return selection
    func _select<Key>(_ key:Key, _ body:(Int) throws -> ()) rethrows 

for some reason the call to _select(_:_:) inside the second _select(_:where:) method can’t choose between _select(_:_:) and the Bool-predicated _select(_:where:) overload, even though _select(_:_:) is the only choice that returns the expected Void return type.

it works when i explicitly coerce the result to as (). why is this needed?

Simplified your example a bit:

extension Sequence {
    func a(where predicate: (Int) throws -> Bool) rethrows -> [Int] { [1, 2, 3] }
    func a(_ body: (Int) throws -> ()) rethrows { }

    func b(where filter: (Int) throws -> Int) rethrows -> [Int] {
        try a {
            let selected = try filter($0)
        return [1, 2, 3]
1 Like

The Void type is not expected there, though. A statement expression discards its result, it does not require that the expression return Void. Perhaps it would be better to give the overloads different names or argument labels instead?


but it is not @discardableResult? surely when given the choice between an overload that generates a warning and an overload that wouldn’t, the compiler ought to prefer the latter?

Perhaps, but philosophically, overload ranking rules are a bit like implicit conversions. They can interact in surprising and counter-intuitive ways, no matter how many you add there is always the temptation to introduce more, and they make the language and implementation harder to understand in the long run.