UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Why is compactMap { $0 } not equivalent to compactMap(\.self)?

Forums > Swift

In trying to understand KeyPath I've been experimenting with this example:

let names: [String] = ["Chris", "Claire", "Finny"]
let optionalNames: [String?] = ["Chris", nil, "Claire"]

let mapped1 = names.compactMap { $0 }
let mapped2 = optionalNames.compactMap { $0 }
// let mapped3 = optionalNames.compactMap(\.self)

print(mapped1)
print(mapped2)
// print(mapped3)

This behaves just like I expect. But when I uncomment the line with let mapped3 = optionalNames.compactMap(\.self) I get the following issues which I'm not sure how to interpret:

error: Playground.playground:6:15: error: generic parameter 'ElementOfResult' could not be inferred let mapped3 = optionalNames.compactMap(.self)

Swift.Sequence:2:28: note: in call to function 'compactMap'

@inlinable public func compactMap<ElementOfResult>(_ transform: (Self.Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]

error: Playground.playground:6:40: error: cannot convert value of type 'WritableKeyPath<, >' to expected argument type '(String?) throws -> ElementOfResult?'

let mapped3 = optionalNames.compactMap(.self)

error: Playground.playground:6:40: error: cannot infer key path type from context; consider explicitly specifying a root type

let mapped3 = optionalNames.compactMap(.self)

My (apparently flawed) understanding of KeyPaths would suggest that mapped2 and mapped3 should be equivalent but apparently they're not.

If I try this

struct Person {
    let name: String
 }

let persons = [
    Person(name: "Mark"),
    Person(name: "Emma"),
]

let mapped4 = persons.map { $0.name }
let mapped5 = persons.map(\.name)

print(mapped4)
print(mapped5)

I get, as expected, the following output:

["Mark", "Emma"]

["Mark", "Emma"]

But if I replace the mapped5 definition with let mapped5 = persons.map(\.self) I get the error:

error: Playground.playground:18:23: error: no exact matches in call to instance method 'map'

let mapped5 = persons.map(.self)

Playground.playground:18:23: note: found candidate with type '((Person) throws -> ) throws -> Array<>'

let mapped5 = persons.map(.self)

It seems I'm misunderstanding what a keypath to \.self does here but I don't know how exactly. Could somebody clarify what's going on here?

3      

Aaah, thank you! I thought I was crazy. 😅 Cheers, that explains a lot!

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.