Declaring Closures in Swift

| Comments

The Swift Programming Language was just announced yesterday at Apple’s WWDC 2014 conference. After downloading the iBook of “The Swift Programming Language” and the beta of Xcode 6, I was playing around with the language, but had some difficulting finding a clear on the synax for declaring closures. All I could find were text descriptions, and formal grammar definitions. Both of those take too long for my brain to decode so I whipped up a few concrete examples:

Closures with Named and Explicitly Typed Parameters Return Value

The most verbose syntax for a closure specifies the parameters along with their types and the return type of the closure.

{(param: Type) -> ReturnType in expression_using_params}    

examples:

[3, 4, 5].map({(val: Int) -> Int in val * 2})                     //> [6, 8, 10]    

[1, 2, 3].reduce(0, {(acc: Int, val: Int) -> Int in acc + val})   //> 6

As everything is explicit, you can assign each of these to a constant with let:

let timesTwo = {(val: Int) -> Int in val * 2}
[3, 4, 5].map(timesTwo)       //> [6, 8, 10]    

let sumOf = {(acc: Int, val: Int) -> Int in acc + val}
[1, 2, 3].reduce(0, sumOf)    //> 6

Closures with Named Parameters and Implicit Types

If you’re using your closures as inline parameters to a function, the types can be inferred so you don’t need to explicitly set them.

{(params) in expression_using_params}

examples:

[3, 4, 5].map({(val) in val * 2})                 //> [6, 8, 10]

[1, 2, 3].reduce(0, {(acc, val) in acc + val})    //> 6

Closures with Positionally Named Parameters

If you don’t care about naming your variables for clarity, you can just use 0-based positional arguments (similar to shell script positional args).

[3, 4, 5].map({$0 * 2})           //> [6, 8, 10]

[1, 2, 3].reduce(0, {$0 + $1})    //> 6

Implicit Closure Parameter Limitations

The compiler needs to have enough information about how you’re using the closures so that it can infer the types, so this works because we’re multiplying by 2, so it knows that $0 must also be an Int:

let timesTwo = {$0 * 2}
[3, 4, 5].map(timesTwo)

But this throws an exception because it can’t tell for sure what type of arguments $0 and $1 are:

let sumOf = {$0 + $1}     // FAILS with "could not find an overload for '+' that accepts the supplied arguments let sumOf = {$0 + $1}"
[1, 2, 3].reduce(0, sumOf)

At the least, you need to provide named parameters and their types:

let sumOf = {(acc: Int, val: Int) in acc + val}
[1, 2, 3].reduce(0, sumOf)        //> 6

Comments