Home > Articles > Data > SQL Server

  • Print
  • + Share This
Like this article? We recommend

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

  • + Share This
  • 🔖 Save To Your Account