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)
{
selection.append(selected)
}
} 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?
2 Likes
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.
7 Likes