Why does compactMap return a nil result?
Why does compactMap return a nil result?
Consider this code snippet:
var a: String? = "abc"
var b: String?
let result = [a, b].compactMap $0
After executing it, result
will be
result
["abc"]
which is the expected result. The element of result (ElementOfResult
) here is String
.
ElementOfResult
String
print(type(of: result))
Array<String>
Now to the interesting part. After changing the snippet to
var a: String? = "abc"
var b: Int?
let result = [a, b].compactMap $0
and executing it, result
will be
result
[Optional("abc"), nil]
The element of result (ElementOfResult
) here is Any
which makes sense, because Any
is the common denominator of String
and Int
.
ElementOfResult
Any
Any
String
Int
print(type(of: result))
Array<Any>
Why was a nil
result returned by compactMap
which contradicts its definition?
nil
compactMap
From Apple's compactMap
documentation
compactMap
Returns an array containing the non-nil results of calling the given transformation with each element of this sequence.
func compactMap(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
result
[Any]
[Any?]
@user1046037
compactMap
is there to remove nil. What sense does it make to return [Any?] if compactMap
already made sure none of them is nil?– Purpose
18 hours ago
compactMap
compactMap
2 Answers
2
This is because [a, b]
is considered a [Any]
. When the element types in an array literal are entirely unrelated (Int?
and String?
), the array type is inferred to be [Any]
.
[a, b]
[Any]
Int?
String?
[Any]
In the closure passed to compactMap
, you returned $0
, which is of type Any
. This means that $0
can never be nil
. All the optionals inside the array are all wrapped in an Any
the moment you put them in the array. Because you never return a nil
in the closure, all the elements stay in the result array.
compactMap
$0
Any
$0
nil
Any
nil
The compiler can warn you about wrapping optionals in non-optional Any
s:
Any
var a: String? = "abc"
let any: Any = a // warning!
But unfortunately it doesn't warn you when you create arrays.
Anyway, you can get the expected behaviour by specifying that you want a [Any?]
:
[Any?]
let result = ([a, b] as [Any?]).compactMap $0
So you kind of unwrap them from Any
.
Any
Or:
let result = [a as Any?, b as Any?].compactMap $0
Why can an optional type be wrapped inside an Any
?
Any
According to the docs (In the Type Casting for Any
and AnyObject
section):
Any
AnyObject
Any
can represent an instance of any type at all, including function types.
Any
Thus, Optional<T>
undoubtedly can be represented by Any
.
Optional<T>
Any
Based on the original example, wondering how result can be of the type
[Any]
and still hold a nil
– user1046037
18 hours ago
[Any]
nil
@user1046037 Because anything is
Any
. Any
can hold every type.– Sweeper
18 hours ago
Any
Any
The following code throws a compilation error,
let x : Any = nil
– user1046037
18 hours ago
let x : Any = nil
@user1046037
nil
isn‘t inside the resulting array, but an optional which is nil which only prints nil
.– Purpose
18 hours ago
nil
nil
My bad, Any can be nil,
var y : Int? = nil; let x : Any = y as Any
– user1046037
18 hours ago
var y : Int? = nil; let x : Any = y as Any
You create an Any-array and compactMap over its elements gives compactMap
only Any-elements, no Optional<Any>
which it could think about being nil or not, so all emenents stay.
compactMap
Optional<Any>
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
What is more puzzling is why the type of
result
is[Any]
. I would have expected it to be[Any?]
– user1046037
18 hours ago