I would expect the following to compile, and I'd like to know if the warning and error are incorrect:
let a = [1, 2, 3, 4]
let b = a.flatMap { // WARNING: flatMap' is deprecated: Please use compactMap(_:) for the case where closure returns an optional value
let (q, r) = $0.quotientAndRemainder(dividingBy: 3)
return [q, r] // ERROR: Cannot convert return expression of type '[Int]' to return type 'String?'
}
print(b)
A workaround is to help the compiler by being more explicit about the closure type:
let a = [1, 2, 3, 4]
let b = a.flatMap { (i) -> [Int] in
let (q, r) = i.quotientAndRemainder(dividingBy: 3)
return [q, r]
}
print(b) // [0, 1, 0, 2, 1, 0, 1, 1]
Swift doesn't do type-inference over multiple statements in a closure, so instead it's checking the call to flatMap in one phase, deducing a type for the closure, and then doing a type-check of the closure body in a second phase.
I have no idea how it settled on (Int) -> String? as the type for the closure passed to flatMap, but that's what's happening.
Hmm, is the conclusion that my initial example (repeated below) should compile?
let a = [1, 2, 3, 4]
let b = a.flatMap { // WARNING: flatMap' is deprecated: Please use compactMap(_:) for the case where closure returns an optional value
let (q, r) = $0.quotientAndRemainder(dividingBy: 3)
return [q, r] // ERROR: Cannot convert return expression of type '[Int]' to return type 'String?'
}
print(b)
Nah, it should not compile. Swift only does type inference if the closure has a single statement (i.e what John said). As yours spans multiple statements, you need to provide the type context explicitly, like in your 'workaround' example.
The removal of the obsoleted methods would simply result in a more sensible error message.
Ok ... FWIW pressing ctrl+cmd+J (jump to definition) on flatMap in:
let a = [1, 2, 3, 4]
let b = a.flatMap { // WARNING: flatMap' is deprecated: Please use compactMap(_:) for the case where closure returns an optional value
let (q, r) = $0.quotientAndRemainder(dividingBy: 3)
return [q, r] // ERROR: Cannot convert return expression of type '[Int]' to return type 'String?'
}
print(b)
takes me to the flatMap of enum Optional.
(This happens with both Swift 4.2 and Swift 5 using the latest snapshot.)
Can anyone explain why/how Optional should have anything to do with this?
Wouldn't the removal of the obsoleted methods result in no optional-returning methods at all named flatMap (except eg Optional.flatMap which I assume should not be considered at all as a possible candidate), meaning the only relevant methods named flatMap left to choose from would have the expected signature?
What would the more sensible error message be (approximately)?
It would, but we can only delete the methods obsoleted in Swift 3, because we no longer support the -swift-version 3 mode in the compiler. And the one causing troubles is still only deprecated in 4.1.
In Xcode 10.2 beta 1 I see a popup with additional related options on Optional but the first one does take you to Swift.Collection.Array, though whether it jumps to a visible declaration or not seems to depend on how the closure is written.
If you could file a Jira and provide more details like the configuration and screenshots, it would be helpful to track down.