Just An Application

June 29, 2014

So Swift Then: Tuples

A Swift tuple is a composite value comprised of zero or more values.

An instance of a tuple is written as a parenthesis delimited list of comma separated values, like so

    (1, 2, 3)

The values in a tuple do not have to be of the same type, for example,

    (true, 2, ("three"))

The mutability of a tuple in Swift is determined by whether it has been assigned to a constant or a variable.

1.0 Types

The type of a tuple is a function of the number of values and their types and is written in the same way as a tuple but with type names rather than values.

1.1 Zero Values

The type of an empty tuple is written as an empty tuple, like so

    let t0 : ()  = ()

In Swift the type Void is simply an alias for the type of an empty tuple, so you can also write

    let tv : Void  = ()

1.2 One Value

The type of a single value tuple is simply the type of the value [1], so this

    let t1 : (String)  = ("malin")

can also be written like this

    let t1 : String  = ("malin")

or like this

    let t1 : (String)  = "malin"

or, if you are feeling really adventurous, like this

    let t1 : String  = "malin"

1.3 More Than One Value

The type of a tuple with more than one value is more straight-forward.

For example, the type of the tuple

    (true, 2, ("three"))

is

    (Bool, Int, (String))

2.0 Value Access

2.1 The Index Notation

The values comprising a tuple can be accessed using an index-like notation like this

    let triple  = (1, 2, 3)

    let first   = triple.0

The same notation can be use to modify a mutable tuple, for example

    var mutableTriple = (1, 2, 3)

    mutableTriple.2 = 5

2.1 Pattern Matching

The values comprising a tuple can also be accessed using pattern matching like this

    let triple    = (1, 2, 3)
    let (x, y, z) = triple

which results in x having the value 1, y having the value 2, and z having the value 3.

Values within a tuple can be ignored in a pattern by using an underscore (‘_‘) like so

    let triple    = (1, 2, 3)
    let (_, y, _) = triple

As before, y has the value 2.

Pattern matching can also be used in an assignment statement like this

    let fishes    = ("cod", "dab", "eel")
    var oneFish   = ""
    var twoFish   = ""
    var threeFish = ""

    (oneFish, twoFish, threeFish) = fishes

    (oneFish, _, _) = fishes

3.0 Assignment

Assignment of a tuple results in a copy being made, so

    let fishes        = ("cod", "dab", "eel")
    var mutableFishes = fishes

    mutableFishes.0 = "saithe"

    println(fishes)
    println(mutableFishes)

prints

    (cod, dab, eel)
    (saithe, dab, eel)

4.0 Named Values

The values in a tuple can also be named, like so

    var fishes = (first:"cod", second:"dab", third:"eel")

They can then be accessed by their names

    let first = fishes.first

and if the tuple is mutable also modified

    fishes.first = "saithe"

It is also possible to use the index notation and pattern matching to access the values in a named tuple but this is not necessarily a good idea for reasons that will become apparent.

4.1 Named Values And Tuple Types

It is not entirely clear what the type of a tuple with named values is.

It is apparent that if declared explicitly it must include the names so if you try this

    let namedFishes: (String, String, String) = (first:"cod", second:"dab", third:"eel")

you get a compiler error.

On the other hand this works

    ...
    
    typealias namedFishesType = (first:String, second:String, third:String)
    
    let namedFishes: namedFishesType = ("cod", "dab", "eel")

    ...

as does this

     ...
    
    typealias namedFishesType = (first:String, second:String, third:String)
    
    var namedFishes: namedFishesType = (first:"cod", second:"dab", third:"eel")
    
    ...
    
    var unnamedFishes: (String, String, String) = ("flounder", "gudgeon", "hake")

    namedFishes = unnamedFishes

    ...

and this

    ...
    
    typealias namedFishesType = (first:String, second:String, third:String)
    
    var namedFishes: namedFishesType = (first:"cod", second:"dab", third:"eel")
    
    ...
    
    var unnamedFishes: (String, String, String) = ("flounder", "gudgeon", "hake")
    
    unnamedFishes = namedFishes

so for the purposes of assignment unnamed and named tuple values whose elements are of the same type seem to be interchangeable.

In addition for the purposes of assignment the order of the names in a named tuple value does not seem to be significant, so

    ...
    
    typealias namedFishesType = (first:String, second:String, third:String)
    
    let namedFishes: namedFishesType = (first:"cod", second:"dab", third:"eel")

    println(namedFishes)

    ...

prints

    (cod, dab, eel)

and so does

    ...
    
    typealias namedFishesType = (first:String, second:String, third:String)
    
    let namedFishes: namedFishesType = (third:"eel", second:"dab", first:"cod")
    
    println(namedFishes)

    ...

However, just to make things confusing, the order of the names in a named tuple type does seem to be significant, at least for the purposes of using the index notation and pattern matching, so

    ...
    
    typealias namedFishesType = (first:String, second:String, third:String)
    
    let namedFishes: namedFishesType = (first:"cod", second:"dab", third:"eel")

    println("namedFishes.0 == \(namedFishes.0)")

    let (first, second, third) = namedFishes

    println("\(first) \(second) \(third)")

    ...

prints

    namedFishes.0 == cod
    cod dab eel

but

    ...
    
    typealias reversedFishesType = (third:String, second:String, first:String)
    
    let reversedFishes: reversedFishesType = (first:"cod", second:"dab", third:"eel")

    println("reversedFishes.0 == \(reversedFishes.0)")

    let (first, second, third) = reversedFishes

    println("\(first) \(second) \(third)")

    ...

prints

    reversedFishes.0 == eel
    eel dab cod

which is why you might want to think twice about acessing named values in tuples using the index notation and/or pattern matching.

Finally, as already noted, the types of named values are significant when the names are the same, so you cannot do something like this

    ...
    
    typealias namedFishesType = (first:String, second:String, third:String)
    
    // DOES NOT COMPILE: names match, types do not match
    
    let fishes: namedFishesType = (first:"cod", second:2, third:true)

    ...

as are the names when the types are the same, so you cannot do something like this either

    ...
    
    typealias  namedFishesType = (first:String, second:String, third:String)
    
    // DOES NOT COMPILE: types match, names do not match

    let namedFishes: namedFishesType = (one:"cod", two:"dab", three:"eel")

    ...

Notes

  1. At first glance this seems a trifle odd but there is a possible explanation.

    It may have to do with the function return value type in Swift.

    The clue is the fact that Void is an alias for the type of an empty tuple.


Copyright (c) 2014 By Simon Lewis. All Rights Reserved.

Unauthorized use and/or duplication of this material without express and written permission from this blog’s author and owner Simon Lewis is strictly prohibited.

Excerpts and links may be used, provided that full and clear credit is given to Simon Lewis and justanapplication.wordpress.com with appropriate and specific direction to the original content.

Blog at WordPress.com.