Facets of Swift, Part 2: Tuples

Facets of Swift is a series of articles on various aspects of the Swift programming language. I write these articles to organize my thoughts on Apple’s new programming language.

This second article is about Swift’s tuples. Tuples allow us to write more compact code. In addition to that, tuples are a prerequisite to understanding functions, methods and closures, which will be the topic of later articles.

What is a Tuple?

We may remember the concept of tuples from math or computer science. There, a tuple is simply an ordered list of values, where the type of each value is fixed. In Swift, it’s about the same. To build an n-tuple, we just put the values in parentheses:

var result = (200, "OK", true)

Here result is a 3-tuple that holds three values: 200 (an Int), “OK” (a String) and true (a Bool). The variable result has a tuple type. To declare a tuple type, we use parentheses as well:

var result: (Int, String, Bool)
result = (200, "OK", true)

Accessing Tuple Elements

The most common method of accessing a tuple’s elements is by decomposing the tuple:

var result = (200, "OK", true)
let (statusCode, statusMessage, hasBody) = result

Now the three constants statusCode, statusMessage, and hasBody have the values 200, “OK”, and true. If we are not interested in a value, we can use an underscore instead of an identifier, the so-called wildcard expression:

var result = (200, "OK", true)
let (statusCode, _, _) = result

We can also access the elements of a tuple by their index:

var result = (200, "OK", true)
let statusCode = result.0

If a tuple is variable, we can initialize the tuple by setting the elements:

var result: (Int, String, Bool)
result.0 = 200
result.1 = “OK”
result.2 = true

Naming Tuple Elements

Of course, the last example is not self-documenting: It’s hard to understand what it is about as the elements of the tuple are not named. Well, we can do that:

var result: (statusCode: Int, statusText: String, hasBody: Bool)
result.statusCode = 200
result.statusText = “OK”
result.hasBody = true

To define the variable result in a shorter way, we can also use type inference:

var result = (statusCode: 200, statusText: "OK", hasBody: true)

We don’t have to name all elements of the tuple. You can think of the element names as add-on parts of the tuple type. Provided that tuple length and types match, the assignment will work as long as there are no name conflicts:

var result = (statusCode: 200, statusText: "OK", true)
var result2 = (statusCode: 404, "Not Found", hasBody: true)
result = result2                             // OK
result2 = result // OK
result = (code: 200, statusText: "OK", true) // ERROR

Later on we’ll see why this is a good choice. What we’ve seen so far: Tuples in Swift can be used to group, and optionally name, related values.

Before we dive into the various usage scenarios of tuples in Swift, let’s have a look at two special cases: The tuple with no elements and the tuple with only one element.

Not that Special: The 0-Tuple

As a tuple is a list of elements, can we define an empty tuple, that is a tuple with no elements? Yes we can:

var empty: ()
empty = ()

The code above defines the variable empty to hold the so-called empty tuple type, and afterwards assigns the empty tuple to it. The empty tuple in Swift is not special, it’s just a tuple with no elements.

There is a typealias in Swift for the empty tuple type called Void:

var empty: Void
empty = ()

Note that you can’t use Void as value: It is a type, not a value. So this won’t compile:

var empty: Void    // OK
empty = Void // COMPILER ERROR

The only difference of the empty tuple to a tuple with two or more elements is that the compiler currently gives us a warning if the empty tuple type is inferred from the empty tuple: If you compile this:

var empty = ()

the compiler will give you a warning: “Variable ‘empty’ inferred to have type ‘()’, which may be unexpected”.

Now that we know about the 0-tuple, what about the 1-tuple?

Special: The 1-Tuple

In contrast to the empty tuple or tuples with two elements or more, the 1-tuple is really special. Because there is no 1-tuple in Swift. If we declare

var a: Int
var b: (Int)
var c: ((((((((((Int))))))))))
var d = 42
var e = (42)
var f = ((((((((((42))))))))))

all the variables a to f all have the same type: Int. At first, this may feel like a horrible inconsistency. But it’s not.

First, depending on your background, it may not feel like an inconsistency. In some parts of math and computer science, an n-tuple type is a product of n types: T = T1 x … x Tn. A 1-tuple would consequently be the type itself: T = T1. So with that background, it feels right that there is no 1-tuple.

Second, if parentheses around a single element would return a tuple type, we could not use parentheses to group anything. Here are a two examples:

var mapping: (Int -> Int)?

The variable mapping is an optional function from Int to Int. If parentheses around a single element would return a tuple type, we would not be able to define the type of the variable mapping.

var n = 5
var sumOfNumbersToN = (n * (n + 1)) / 2

sumOfNumbersToN is inferred to the type Int. That’s how it should be. If parentheses around a single element would return a tuple type, we would get in trouble as multiplication and division would have to be defined on tuple types as well, and sumOfNumbersOfN would maybe end up to be a tuple type.

To summarize: There is no 1-tuple in Swift, only the value itself. There is no 1-tuple type in Swift, only the type itself.*

(* but read the bonus track at the end of this article on how the beta 4 compiler disagrees with that)

Now let’s have a look where tuples are used in Swift.

When to Use Tuples

As tuples form a group of elements, they are used in all places where such a group is useful. The first and simplest use for tuples is multiple assignment. Instead of

var x = 1
var y = 2
var z = 3

we can write

var (x, y, z) = (1, 2, 3)

The second use is to return multiple values from a function. Imagine in our app we often need both the result of the division and the remainder for integers a and b:

let quotient = a / b
let remainder = a % b
println(quotient, remainder)

So we define a function that returns both in a tuple:

func divmod(a: Int, b: Int) -> (quotient: Int, remainder: Int) {
return (a / b, a % b)
}

(Sidenote: I am aware that this should rather be an extension on Int, or a custom operator, but those are topics for future articles.)

Now we can decompose the result of the function:

let (quotient, remainder) = divmod(a, b)
println(quotient, remainder)

Alternatively, we can store the tuple result of the call, and access the individual elements:

let result = divmod(a, b)
println(result.quotient, result.remainder)

The above example illustrates why Swift’s designers chose to make assignment work from non-named tuple elements to named tuple elements and vice versa: If we would be forced to match the names, neither the return statement of the function, nor the decomposition of the function’s result would work.

The third use of tuples is when iterating an array or dictionary. Inside the for…in statement, we use tuple decomposition:

for (key, value) in dict {
// ...
}
for (index, value) in enumerate(array) {
// ...
}

Instead of the decomposition, we could get the tuples as well:

for keyValueTuple in dict {
// ...
}
for indexValueTuple in enumerate(array) {
// ...
}

The fourth use of tuples is in switch statements. Swift’s switch statements are very powerful. They can also match tuples. Here is an example showing the power of the matching:

var point = (x: 5, y: 5)
switch point {
case (0, 0): // (1)
println("Origin")
case (_, 0): // (2)
println("On x-axis")
case (0, _): // (2)
println("On y-axis")
case let (x, y) where x == y: // (3)
println("On 1. diagonal")
case let (x, y) where x == -y: // (3)
println("On 2. diagonal")
case (-1...1, -1...1): // (4)
println("Near origin")
default: // (5)
()
}

The above example shows matching a tuple with concrete values for the elements (1), using the wildcard operator (2), using binding and a where statement (3), as well as using ranges (4).

If you wonder about the parentheses after default (5), that’s the empty tuple. In Swift it is not allowed to have a case or default in a switch statement without a following expression, and the empty tuple () is a short, valid expression. You can also use 0 if you prefer.

As we know from C and other languages, default matches everything that’s not handled in the cases above. If Swift would not have a default keyword, we could use case (_, _) or case _ instead.

When Not to Use Tuples

At the time of writing, Swift is only a couple of weeks old, so take the following recommendations with a grain of salt.

Tuples allow us to group related values easily, but we should not use them to replace better suited data structures. There are some indications when you may prefer a data structure to a tuple type:

  • The strongest indication is that you have a good name for the group of elements of a tuple. Then choose a struct or class with that name.
  • If you would like to define functions that take the tuple as a parameter, try to come up with a name for a struct or class, and define the function as a method on that type.
  • If the tuple contains elements which are optional and mutually exclusive: An enum is a better way to express that.

That’s it for this article. I tried to only show what’s documented about Swift’s tuples. However, there are some things I stumbled upon when writing the fourth part of this series, which I summarized in the following section. Beware: It is not needed to do Swift programming, it’s purely based on pushing the boundaries of a new language that’s still in flux. It may be confusing. So you may skip the last section and continue reading the next article of this series, where we will have a look at values and references in Swift.

Bonus Track: Tuples All the Way Down

Still here? Great! Let’s have a look at a variable declaration:

var foo: (Int) = 42

The Swift Programming Language iBook tells us if we use (Int) it is simply Int, not (Int). Also, it tells us that as a result, we can only name tuple elements if there are at least two elements.

Well, let’s see whether the compiler disagrees with the documentation:

var foo: (x: Int) = 42
println(foo.x)
println(foo.0)

It does: This works fine and prints 42 two times. So yes, we can name tuple elements if there is only one element in the tuple, and yes, there seems to be a named 1-tuple in Swift. In the fourth article of this series, we’ll see an explanation why the existence of a named 1-tuple makes sense.

Let’s check again whether there is a 1-tuple:

var foo: (Int) = 42
println(foo.0)

Wait, what? That works fine as well. The documentation says (Int) is simply Int. Maybe it’s the other way round? Maybe Int is (Int)? Maybe everything is a tuple?

var foo: Int = 42
println(foo.0)

Works fine as well. But that’s not all. If Int is (Int) then shouldn’t it by induction also be ((Int))? Or even ((((((((((Int))))))))))?

var foo: Int = 42
println(foo.0.0)
println(foo.0.0.0.0.0.0.0.0.0.0)

It is. In Swift, there are tuples all the way down. At least in beta 4.


In the next article of this series, we will have a look at values and references in Swift.

Show your support

Clapping shows how much you appreciated Tammo Freese’s story.