2

I'm reading "The swift programming language 4.2". In the beginning chapter, page 23, I have this following requirement:

Rewrite the closure to return zero for all odd number

And my solution is:

let myArray: [Int] = [1, 2, 3, 4, 5]

myArray.map({ (number: Int) in 
   if number % 2 != 0 {
       return 0
   } else {
       return number
   }
})

But I have this following error:

Ambiguous reference to member 'map'

I really do not understand why I'm wrong, why my 'myArray' can not references to 'map' member? Could you give me and explanation of this error and the right solution for this? Thank you!

pacification
  • 5,838
  • 4
  • 29
  • 51
phuongzzz
  • 85
  • 1
  • 10

2 Answers2

3

Make it less ambiguous by specifying the return type with as [Int]:

myArray.map({ (number: Int) in 
   if number % 2 != 0 {
       return 0
   } else {
       return number
   }
}) as [Int]

Or :

let result: [Int] = myArray.map({ (number: Int) in
    if number % 2 != 0 {
        return 0
    } else {
        return number
    }
})
print(result) //[0, 2, 0, 4, 0]

As noted by vadian: The ambiguity comes from the fact that the generic return type in the closure cannot be inferred.

To understand how the compiler infers the return type of a closure, let's go back to the syntax of a closure :

let myClosure: returnType = { (params) -> returnType in
    statements
}

Here, the type of myClosure is returnType. And it's set in two places: after :, and after ->. You could use type inference by removing the returnType from one of the two places, but not both.

So you could remove it from inside the curly braces (like in the code above) :

let result: [Int] = myArray.map({ (number: Int) in

Or just after the variable name, and specifying the return type of the closure inside the the curly braces:

let result = myArray.map({ (number: Int) -> Int in
ielyamani
  • 17,807
  • 10
  • 55
  • 90
  • Where are you returning the number to? I don't see a return type anywhere. – Rakesha Shastri Sep 01 '18 at 17:59
  • Thank you very much. Unfortunately, I can not upvote your answer because of the new member policy :( – phuongzzz Sep 01 '18 at 18:01
  • @yesterdayoncemore Glad I could help – ielyamani Sep 01 '18 at 18:03
  • *The ambiguity comes from the fact* that the generic return type in the closure cannot be inferred. A type can be `map`ped to any other even unrelated type. By the way you are discouraged from using the first (bridge cast) syntax. – vadian Sep 01 '18 at 18:12
  • @vadian In this case, only two types are possible: `[Int]` and `[Any]`. Yet, why isn't type inference working? – ielyamani Sep 01 '18 at 18:18
  • No, you can also return `"0"` and `String(number)`. Then the return type is `[String]` (in the closure `String`). That's the huge benefit of generics (the return type of the closure is `T`, a generic type). – vadian Sep 01 '18 at 18:23
  • @vadian returning strings isn't the point. in the original code, both return values are ints: 0 and number. It's probably a bug. – ielyamani Sep 01 '18 at 18:26
  • 1
    It's not a bug. If you specify the full closure you have to specify also the (static) return type of the **closure** (like in my answer) or to annotate the return type of the **function** like in your second example. In the latter case the compiler infers the closure return type from the annotation. – vadian Sep 01 '18 at 18:29
  • 1
    @yesterdayoncemore [This](https://stackoverflow.com/help/someone-answers) could be helpful – ielyamani Sep 01 '18 at 18:50
2

Rather than annotating the parameter type you have to specify the return type of the closure

let myArray = [1, 2, 3, 4, 5] // You don't even need to annotate the array, the compiler knows the type.

let result = myArray.map({ number -> Int in
    if number % 2 != 0 {
        return 0
    } else {
        return number
    }
})

Or with trailing closure syntax and shorthand argument names. In this case the compiler can infer everything

let result = myArray.map { $0 % 2 != 0 ? 0 : $0 }
vadian
  • 274,689
  • 30
  • 353
  • 361
  • Thank for your help! Another nice solution that I can have! :) – phuongzzz Sep 01 '18 at 18:06
  • The “automatic inference” is unrelated to trailing closure syntax or shorthand argument names, `myArray.map({ number in number % 2 != 0 ? 0 : number })` does compile as well. For *single expression closures* the return type is inferred automatically, compare https://stackoverflow.com/a/34115788/1187415 (and I am tempted to close question this as a duplicate). – Martin R Sep 01 '18 at 18:55