Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swit map: error: cannot invoke 'map' with an argument list of type '((_) -> _)'

Tags:

ios

swift

I can't understand why this one works:

var arr = [4,5,6,7]

arr.map() {
    x in
    return  x + 2
}

while this one not

arr.map() {
    x in
    var y = x + 2
    return y
}

with error

Playground execution failed: MyPlayground.playground:13:5: error: cannot invoke 'map' with an argument list of type '((_) -> _)' arr.map() {

like image 820
user1284151 Avatar asked Dec 25 '22 14:12

user1284151


2 Answers

The problem here is there error message. In general, when you see something like cannot invoke .. with ... it means that the compiler's type inference has just not worked.

In this case, you've run up against one of the limitations of inference within closures. Swift can infer the type of single-statement closures only, not multiple-statement ones. In your first example:

arr.map() {
  x in
  return  x + 2
}

There's actually only one statement: return x + 2. However, in the second:

arr.map() {
  x in
  var y = x + 2
  return y
}

There's an assignment statement (var y = x + 2), and then the return. So the error is a little misleading: it doesn't mean you "can't invoke map() with this type of argument", what it means to say is "I can't figure out what type x or y is".

By the way, in single-statement closures, there are two other things that can be inferred. The return statement:

arr.map() {
  x in
  x + 2
}

And the variable name itself:

arr.map() { $0 + 2 }

It all produces the same compiled code, though. So it's really a matter of taste which one you choose. (For instance, while I think the inferred return looks clean and easier to read, I don't like the $0, so I generally always put x in or something, even for very short closures. It's up to you, though, obviously.)

One final thing: since this is all really just syntax stuff, it's worth noting that the () isn't needed either:

arr.map { x in x + 2 }

As @MartinR pointed out, the compiler can infer some types from outer context as well:

let b: [Int] = arr.map { x in
  var y = x + 2
  return y 
}

Which is worth bearing in mind. (it seems that the "one-statement" rule only applies when there's no other type info available)

like image 82
oisdk Avatar answered Dec 27 '22 02:12

oisdk


Swift can't infer type every time. Even though it should see that y = x + 2 means y is an Int too. My guess is that Swift parses the closure in a certain order that makes it not aware of the return type ahead of time in your case.

This works:

arr.map() {
    x -> Int in
    var y = x + 2
    return y
}
like image 44
Code Different Avatar answered Dec 27 '22 02:12

Code Different