Tuples

If you have a data representation that involves multiple pieces of information, you may want to construct a data type to represent it. For example, you may want to store people's names as well as their favorite movies. It would be burdensome to have all of this information stored separately because that would be a lot to manage. If our data has limited functionality you may want to use tuples! Tuples are pretty similar to tuples of other languages. OCaml programmers, think records, but you can opt to not give fields names.

We describe a tuple's type by concatenating the interior types with commas in parenthesis:

let raahi = ("Raahi", "Menon", "Grease 2")
// raahi: (String, String, String)

Elements

Elements of a tuple are, by default, accessed by their location, but not through subscription. They are fields, so you access them as you would a variable:

// raahi: { .0: String, .1: String, .2: String }
// = { .0: "Raahi", .1: "Menon", .2: "Grease 2" }

raahi.2
// "Grease 2": String

Similarly, we can assign values through their position:

raahi.0 = "Rocky"
// raahi: { .0: String, .1: String, .2: String }
// = { .0: "Rocky", .1: "Menon", .2: "Grease 2" }

Note that tuples are value types, which means there's references or pointers. This means if we assign one tuple to two locations, we will be creating two copies of the tuples. In other words, if we change one tuple, it will not change the other.

let aBasicTuple = (0, 0)
// aBasicTuple: (Int, Int) = { .0: 0, .1: 0 }

let tupleCopy = aBasicTuple
// tupleCopy: (Int, Int) = { .0: 0, .1: 0 }

aBasicTuple.0 = 1
// aBasicTuple: (Int, Int) = { .0: 1, .1: 0 }
// tupleCopy: (Int, Int) = { .0: 0, .1: 0 }

Element Naming

When tuples get particularly large and/or complex, it can be confusing as to what piece of information is being stored where–types can help, but they aren't always enough. To fix this, we can actually name our positions!

let raahi = (firstName: "Raahi", lastName: "Menon", favoriteMovie: "Grease 2")
// raahi: (firstName: String, lastName: String, favoriteMovie: String) = ...

raahi.favoriteMove
// "Grease 2": String

When we name our positions, the type declaration of the tuple changes, but this is cosmetic. Tuples with elements of the same type are always the same type, regardless of naming conventions.

let raahi = (firstName: "Raahi", lastName: "Menon", favoriteMovie: "Grease 2")
// raahi: (firstName: String, lastName: String, favoriteMovie: String) = ...

let raahiCopy: (String, String, String) = raahi
// raahiCopy: (String, String, String) = 
// { .0: "Raahi", .1: "Menon", .2: "Grease 2"}

Advanced Tuples

These tools are for projects that use tuples extensively and creatively. You can see this in our implementation of dictionaries.

It can get annoying to write a tuple's type a lot. It's quite a lot of code per use of the tuple, it's hard to read, and it's is an error-prone process. We can fix all of this by renaming types using typealias.

typealias Person = (firstName: String, lastName: String, favoriteMovie: String)

let raahi = ("Raahi", "Menon", "Grease 2")
// raahi: Person = ...

As with most types, a typealiased tuple has an initializer (it's just syntactic sugar, but we can think of it that way).

let raahi = Person(firstName: "Raahi", lastName: "Menon", favoriteMovie: "Grease 2")
let raahi2 = Person("Raahi", "Menon", "Grease 2")
// raahi: Person = ...

raahi == raahi2
// true: Bool

typealias also allows for generics!

typealias Element<K, V> = (key: K, value: V)

Last updated