4
let test = [4, 5, 3, 1, 3]
print(
    test.map { $0 }
)
print(
    test.map(\.self)  // Doesn't compile  
)

Error:

Type of expression is ambiguous without more context

Why doesn't it work? Seems like it should.
If isn't this way, how else can we rid of the ugly { $0 } here?

Maybe an example with compactMap will make more cense))

let test = [4, 5, nil, 3, 1, nil, 3]
print(
    test.compactMap { $0 }
)
print(
    test.compactMap(\.self)  // Doesn't compile  
)

Error:

Cannot convert value of type 'WritableKeyPath<_, _>' to expected argument type '(Int?) throws -> ElementOfResult?'

Roman
  • 1,309
  • 14
  • 23
  • 1
    How about writing your own `identity` function? – Sweeper Sep 04 '20 at 02:59
  • @Sweeper That's an interesting idea))) But why doesn't KeyPath work here? – Roman Sep 04 '20 at 03:02
  • 1
    From the way the [docs](https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID563) is worded, it seems like `\.self` is somewhat of a special case, and is treated differently. It even has its own name, "the identity key path". – Sweeper Sep 04 '20 at 03:05
  • What is the “ugly” part? Seems normal to me. – matt Sep 04 '20 at 03:05
  • @matt Yea, probably I'm too biased)) – Roman Sep 04 '20 at 03:08
  • If you think the word `identity` is too long, you could even define your own prefix/postfix operator that acts as an identity function and pass that in... But I doubt that would not be confusing... – Sweeper Sep 04 '20 at 03:10
  • I've seen the same problem in Java. You *could* write `Function::Identity()`, but `x -> x` is so much shorter/quick/clearer. Same here, you could write `func identity(_ arg: T) -> { arg }`, but it's longer to spell that than just `{ $0 }` – Alexander Sep 04 '20 at 03:22
  • 1
    It looks like a bug to me. According to [SE-0249 Key Path Expressions as Functions](https://github.com/apple/swift-evolution/blob/master/proposals/0249-key-path-literal-function-expressions.md) (which is implemented in Swift 5.2) should `let filtered = [1, nil, 3, nil, 5].compactMap(\.self)` compile. But it does not (tested with Xcode 11 and 12 beta). – Martin R Sep 04 '20 at 03:41
  • 2
    It *is* a bug: https://bugs.swift.org/browse/SR-12897 – Martin R Sep 04 '20 at 03:56

1 Answers1

8

Why doesn't it work? Seems like it should.

You are right. Both of your examples should compile because

In fact the latter proposal explicitly mentions an example similar to yours. However, that does not compile (as of Xcode 11.7 and Xcode 12 beta):

[1, nil, 3, nil, 5].compactMap(\.self)
// error: generic parameter 'ElementOfResult' could not be inferred

That is a bug. It has already been reported as

The bug report mentions a custom extension as a possible workaround:

extension Optional { var mySelf: Self { self } }

[1, nil, 3, nil, 5].compactMap(\.mySelf)
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382