- Tuples as Structs
- From Tuples to Structs
- Tuple Limitations
- Tuple Types
- Assigning Tuples
- Passing Tuples as Arguments
- Wrap-Up
Passing Tuples as Arguments
The following closure uses the same signature as all the tuples seen so far, namely (Int, String, Double):
var myclosure = {(a:Int, b:String, c:Double)->() in println("\(a) \(b) \(c)")}
You can call this closure with myclosure(2, "Hello", 5.2). You can also call it with both params1 and params2, which are the unlabeled tuples:
myclosure(2, "Hello", 5.2) myclosure(params1) myclosure(params2) // myclosure(params3) // error
Swift errors if you pass a labeled tuple to myclosure. The tuple type (a:Int, b:String, c:Double) doesn't match the closure's expected parameters (Int, String, Double), even though that type is identical to the one used to construct the closure.
Here's where it gets a little stranger. You can rework the closure to accept a typed tuple using the LabeledTupleType type alias:
var myclosure2:LabeledTupleType->() = {println("\($0.a) \($0.b) \($0.c)")}
With this approach, the closure works for both labeled and unlabeled parameter types:
for tuple in [params1, params2, params3, params4, params5] {myclosure2(tuple)} // works!
Swift uses type inferencing to enable unlabeled tuples to parameterize this closure, even though each field is addressed with the labels a, b, and c.
There are consequences when changing closure typing like this. You cannot call myclosure2 using normal arguments. You need to tupleize your arguments as in the following examples:
// myclosure2(5, "Anonymous", 9.8) // does not work // myclosure2(a:5, b:"Labeled", c:9.8) // does not work myclosure2((a:5, b:"ParamTuple", c:9.8)) // works myclosure2((5, "AnonymousTuple", 9.8)) // works
As before, you can use anonymous or labeled tuples, but the tuple labels must match those defined in the LabeledTupleType type.
If you want to get especially funky, you can mix up the parameter order, and it still works:
myclosure2((c:5.2, b:"Hello", a:6))
Neat, huh?
Functions
Here is a function similar to the previous closures. It expects three arguments using the same Int, String, and Double types:
func myfunction(a: Int, b:String, c:Double){println("\(a) \(b) \(c)")}
You can call this function using myfunction(5, "Ciao", 9.8) but not myfunction(a:5, b:"Bye", c:9.8). The Swift compiler complains about extraneous argument labels in the latter example because functions default to unlabeled parameters.
Unlike with closures, you can adjust function (and method) parameters to force or omit labels from their calling signature. To mandate labels for each parameter, add a hash mark before each label:
func myfunction2(#a: Int, #b:String, #c:Double){println("\(a) \(b) \(c)")}
The first of these two functions (myfunction) works with all the parameter tuples previously defined, including both anonymous and labeled varieties:
for tuple in [params1, params2, params3, params4, params5] {myfunction(tuple)}
This is not a universal solution; it won't work with tuples whose labels don't match the declared labels exactly:
let otherTuple = (x: 5, y:"Hello", z:6.6) myfunction(otherTuple) // does not work
Once you add mandatory labels, as in myfunction2, unlabeled tuples stop working. Only those tuples whose types contain labels are compatible:
// for tuple in [params1, params2] {myfunction2(tuple)} // does not work for tuple in [params3, params4, params5] {myfunction2(tuple)} // works
The tuple labels must also be in order. You cannot call myfunction2 with (c:99.0, a:2, b:"Robin").
Tuple Return Types
Functions, methods, and closures may return tuples. Tuples provide an excellent way to group related information together as an informal anonymous struct:
func MyReturnTuple() -> (a:Int, b:String, c:Double) { return (1, "Hello", 2.2) }
MyReturnTuple() returns a labeled tuple, whose fields you can access with either .0, .1, and .2 or .a, .b, and .c.
For a more meaningful example, a web service method might return an HTTP status code tuple such as (404, "Page not Found"):
func fetchWebStatus(url : NSURL) -> (Int, String) { // …function code here… return (418, "I'm a Teapot (see RFC 2324)") }
You decompose tuples by assignment:
let returnValues = fetchWebStatus() // returns tuple let (statusCode, statusMessage) = returnValues // breaks tuple into components
When you're only interested in some of the tuple values, use the underscore (_) wildcard expression to skip specific assignments. To fetch the status message by position, instead of saying this:
let statusMessage = returnValues.1
use this:
let (_, statusMessage) = returnValues
Functions with Tuple Arguments
Function declarations allow you to use tuple types. The tuple acts as an intermediate argument, so it lacks the elegance of a standard function:
func myfunction3(tuple:UnlabeledTupleType){println("\(tuple.0) \(tuple.1) \(tuple.2)")} func myfunction4(tuple:LabeledTupleType){println("\(tuple.a) \(tuple.b) \(tuple.c)")}
You can call both versions with all five example parameters:
For tuple in [params1, params2, params3, params4, params5] {myfunction3(tuple)} for tuple in [params1, params2, params3, params4, params5] {myfunction4(tuple)}
When working with tupled arguments, as in the following examples, add parentheses to create explicit tuples:
// myfunction3(5, "X", 2.2) // does not work // myfunction4(a:5, b:"X", c:2.2) // does not work myfunction3((5, "X", 2.2)) // works myfunction4((a:5, b:"X", c:2.2)) // works
You improve readability by performing a tuple assignment within the function, but the extra work defeats the point of using a tuple type:
Func myfunction5(tuple : LabeledTupleType) { let (a, b, c) = tuple println("\(a) \(b) \(c)")}
Geek Tuple Overview
For the sake of obsessive semi-completeness, Table 1 summarizes whether arguments will or will not compile over a range of function signatures. The named tuple variables are assigned as follows:
let unlabeledTuple = (2, 3) let labeledTuple = (a:2, b:3)
Table 1. A Geek Tuple Challenge Chart
Example |
(2,3) |
((2, 3)) |
unlabeledTuple |
labeledTuple |
func fa(Int, Int) {} |
Works |
Fails |
Works |
Fails |
func fb((Int, Int)) {} |
Fails |
Works |
Works |
Works |
func fc(_ : (Int, Int)) {} |
Fails |
Works |
Works |
Works |
func fd(a : Int, b : Int) {} |
Works |
Fails |
Works |
Fails |
func fe(#a : Int, #b : Int) {} |
Fails |
Fails |
Fails |
Works |
func ff((a : Int, b : Int)) {} |
Fails |
Works |
Works |
Works |