3

When using map() with enumerate(), Swift will decompose the enumerate tuple:

map(enumerate([1,2,3])) { (index, element) in
    index + element
}

However, this does not appear to work alongside an additional closure parameter (e.g., with reduce()):

reduce(enumerate([1,2,3]), 0) { (accum, (index, element)) in
    accum + index + element
}

This fails with error: use of undeclared type 'index'.

Am I missing something simple, or does Swift simply not allow decomposing a tuple alongside an additional parameter? I have tried this in 1.1 and 1.2. (For now, I am using the shorthand argument names.)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278

1 Answers1

2

The answer is "Swift simply not allow it". You can't decompose nested tuple like that.

For example, this code compiles, but we cannot access j or k:

func foo(i: Int, (j: Int, k: Int) ) {
    // println(j)
    //         ^ error: use of unresolved identifier 'j'
}
foo(1, (2, 3))

Because, in this code, (j: Int, k: Int) is just a type, and the received tuple itself does not have a name. You have to write like this instead:

func foo(i: Int, x: (j: Int, k: Int) ) {
    println(x.j)
}
foo(1, (1, 2))

In the same way, this compiles, but it's useless.

reduce(enumerate([12,42,84]), 0) { (accum, (index:Int, element:Int)) in

Instead, you have to receive the tuple itself, then decompose it if you want:

reduce(enumerate([12,42,84]), 0) { (accum, enumerated)  in
    println(enumerated.element)

    let (index, element) = enumerated
rintaro
  • 51,423
  • 14
  • 131
  • 139
  • I can understand why this would never work at all, such that you would _always_ have to receive the tuple and decompose it. But why does this work in the map() case? When Swift does type inference in that case, is it able to infer that the closure should accept two parameters instead of the usual one (i.e., it's not really doing decomposition)? (Interestingly, if you use the shorthands in the map case, you can use either $0, $1 or $0.0, $0.1.) Thanks. – Darius Samerotte Mar 08 '15 at 09:38
  • That's why I said "nested" :) I don't know why, and AFAIK, it's not clearly documented. – rintaro Mar 08 '15 at 09:43
  • Why does it work? [Swift enumerate functions. How does it work behind?](http://stackoverflow.com/questions/28924967/swift-enumerate-functions-how-does-it-work-behind/28925196#28925196.) – Bartłomiej Semańczyk Mar 08 '15 at 10:02
  • It looks like that just explains why you can call map() on enumerate(). If enumerate is confusing, we can ignore it entirely: `map([(0.0, 0.1, 0.2), (1.0, 1.1, 1.2)]) { (x, y, z) in x + y + z }`. Aside from some weird REPL precision issues (0.30000000000000004 and 3.2999999999999998), that works as well. – Darius Samerotte Mar 08 '15 at 11:02