# F Sharp Programming/Tuples and Records

Jump to navigation Jump to search
 F# : Tuples and Records

## Defining Tuples

A tuple is defined as a comma separated collection of values. For example, `(10, "hello")` is a 2-tuple with the type `(int * string)`. Tuples are extremely useful for creating ad hoc data structures which group together related values. Note that the parentheses are not part of the tuple but it is often necessary to add them to ensure that the tuple only includes what you think it includes.

```let average (a, b) =
(a + b) / 2.0
```

This function has the type `float * float -> float`, it takes a `float * float` tuple and returns another `float`.

```> let average (a, b) =
let sum = a + b
sum / 2.0;;

val average : float * float -> float

> average (10.0, 20.0);;
val it : float = 15.0
```

Notice that a tuple is considered a single argument. As a result, tuples can be used to return multiple values:

Example 1 - a function which multiplies a 3-tuple by a scalar value to return another 3-tuple.

```> let scalarMultiply (s : float) (a, b, c) = (a * s, b * s, c * s);;

val scalarMultiply : float -> float * float * float -> float * float * float

> scalarMultiply 5.0 (6.0, 10.0, 20.0);;
val it : float * float * float = (30.0, 50.0, 100.0)
```

Example 2 - a function which reverses the input of whatever is passed into the function.

```> let swap (a, b) = (b, a);;
val swap : 'a * 'b -> 'b * 'a

> swap ("Web", 2.0);;
val it : float * string = (2.0, "Web")

> swap (20, 30);;
val it : int * int = (30, 20)
```

Example 3 - a function which divides two numbers and returns the remainder simultaneously.

```> let divrem x y =
match y with
| 0 -> None
| _ -> Some(x / y, x % y);;

val divrem : int -> int -> (int * int) option

> divrem 100 20;; (* 100 / 20 = 5 remainder 0 *)
val it : (int * int) option = Some (5, 0)

> divrem 6 4;; (* 6 / 4 = 1 remainder 2 *)
val it : (int * int) option = Some (1, 2)

> divrem 7 0;; (* 7 / 0 throws a DivisionByZero exception *)
val it : (int * int) option = None
```

Every tuple has a property called arity, which is the number of arguments used to define a tuple. For example, an `int * string` tuple is made up of two parts, so it has an arity of 2, a `string * string * float` has an arity of 3, and so on.

### Pattern Matching Tuples

Pattern matching on tuples is easy, because the same syntax used to declare tuple types is also used to match tuples.

Example 1

Let's say that we have a function `greeting` that prints out a custom greeting based on the specified name and/or language.

```let greeting (name, language) =
match (name, language) with
| ("Steve", _) -> "Howdy, Steve"
| (name, "English") -> "Hello, " + name
| (name, _) when language.StartsWith("Span") -> "Hola, " + name
| (_, "French") -> "Bonjour!"
| _ -> "DOES NOT COMPUTE"
```

This function has type `string * string -> string`, meaning that it takes a 2-tuple and returns a string. We can test this function in fsi:

```> greeting ("Steve", "English");;
val it : string = "Howdy, Steve"
> greeting ("Pierre", "French");;
val it : string = "Bonjour!"
> greeting ("Maria", "Spanish");;
val it : string = "Hola, Maria"
> greeting ("Rocko", "Esperanto");;
val it : string = "DOES NOT COMPUTE"
```

Example 2

We can conveniently match against the shape of a tuple using the alternative pattern matching syntax:

```> let getLocation = function
| (0, 0) -> "origin"
| (0, y) -> "on the y-axis at y=" + y.ToString()
| (x, 0) -> "on the x-axis at x=" + x.ToString()
| (x, y) -> "at x=" + x.ToString() + ", y=" + y.ToString() ;;

val getLocation : int * int -> string

> getLocation (0, 0);;
val it : string = "origin"
> getLocation (0, -1);;
val it : string = "on the y-axis at y=-1"
> getLocation (5, -10);;
val it : string = "at x=5, y=-10"
> getLocation (7, 0);;
val it : string = "on the x-axis at x=7"
```

### `fst` and `snd`

F# has two built-in functions, `fst` and `snd`, which return the first and second items in a 2-tuple. These functions are defined as follows:

```let fst (a, b) = a
let snd (a, b) = b
```

They have the following types:

```val fst : 'a * 'b -> 'a
val snd : 'a * 'b -> 'b
```

Here are a few examples in FSI:

```> fst (1, 10);;
val it : int = 1
> snd (1, 10);;
val it : int = 10
> fst ("hello", "world");;
val it : string = "hello"
> snd ("hello", "world");;
val it : string = "world"
> fst ("Web", 2.0);;
val it : string = "Web"
> snd (50, 100);;
val it : int = 100
```

### Assigning Multiple Variables Simultaneously

Tuples can be used to assign multiple values simultaneously. This is the same as tuple unpacking in Python. The syntax for doing so is:

```let val1, val2, ... valN = (expr1, expr2, ... exprN)
```

In other words, you assign a comma-separated list of N values to an N-tuple. Here's an example in FSI:

```> let x, y = (1, 2);;

val y : int
val x : int

> x;;
val it : int = 1

> y;;
val it : int = 2
```

The number of values being assigned must match the arity of tuple returned from the function, otherwise F# will raise an exception:

```> let x, y = (1, 2, 3);;

let x, y = (1, 2, 3);;
------------^^^^^^^^

stdin(18,13): error FS0001: Type mismatch. Expecting a
'a * 'b
but given a
'a * 'b * 'c.
The tuples have differing lengths of 2 and 3.
```

### Tuples and the .NET Framework

From a point of view F#, all methods in the .NET Base Class Library take a single argument, which is a tuple of varying types and arity. For example:

Class C# Function Signature F# Function Signature
`System.String` `String Join(String separator, String[] value)` `val Join : (string * string array) -> string`
`System.Net.WebClient` `void DownloadFile(String uri, String fileName)` `val DownloadFile : (string * string) -> unit`
`System.Convert` `String ToString(int value, int toBase)` `val ToString : (int * int) -> string`
`System.Math` `int DivRem(int a, int b, out int remainder)` `val DivRem : (int * int) -> (int * int)`
`System.Int32` `bool TryParse(String value, out int result)` `val TryParse : string -> (bool * int)`

Some methods, such as the `System.Math.DivRem` shown above, and others such as `System.Int32.TryParse` return multiple through output variables. F# allows programmers to omit an output variable; using this calling convention, F# will return results of a function as a tuple, for example:

```> System.Int32.TryParse("3");;
val it : bool * int = (true, 3)

> System.Math.DivRem(10, 7);;
val it : int * int = (1, 3)
```

## Defining Records

A record is similar to a tuple, except it contains named fields. A record is defined using the syntax:

```type recordName =
{ [ fieldName : dataType ] + }
```
`+` means the element must occur one or more times.

Here's a simple record:

```type website =
{ Title : string;
Url : string }
```

Unlike a tuple, a record is explicitly defined as its own type using the `type` keyword, and record fields are defined as a semicolon-separated list. (In many ways, a record can be thought of as a simple class.)

A `website` record is created by specifying the record's fields as follows:

```> let homepage = { Title = "Google"; Url = "http://www.google.com" };;
val homepage : website
```

Note that F# determines a records type by the name and type of its fields, not the order that fields are used. For example, while the record above is defined with `Title` first and `Url` second, it's perfectly legitimate to write:

```> { Url = "http://www.microsoft.com/"; Title = "Microsoft Corporation" };;
val it : website = {Title = "Microsoft Corporation";
Url = "http://www.microsoft.com/";}
```

It's easy to access a record's properties using dot notation:

```> let homepage = { Title = "Wikibooks"; Url = "http://www.wikibooks.org/" };;

val homepage : website

> homepage.Title;;
val it : string = "Wikibooks"

> homepage.Url;;
val it : string = "http://www.wikibooks.org/"
```

### Cloning Records

Records are immutable types, which means that instances of records cannot be modified. However, records can be cloned conveniently using the clone syntax:

```type coords = { X : float; Y : float }

let setX item newX =
{ item with X = newX }
```

The method `setX` has the type `coords -> float -> coords`. The `with` keyword creates a clone of `item` and set its `X` property to `newX`.

```> let start = { X = 1.0; Y = 2.0 };;
val start : coords

> let finish = setX start 15.5;;
val finish : coords

> start;;
val it : coords = {X = 1.0;
Y = 2.0;}
> finish;;
val it : coords = {X = 15.5;
Y = 2.0;}
```

Notice that the `setX` creates a copy of the record, it doesn't actually mutate the original record instance.

Here's a more complete program:

```type TransactionItem =
{ Name : string;
ID : int;
ProcessedText : string;
IsProcessed : bool }

let getItem name id =
{ Name = name; ID = id; ProcessedText = null; IsProcessed = false }

let processItem item =
{ item with
ProcessedText = "Done";
IsProcessed = true }

let printItem msg item =
printfn "%s: %A" msg item

let main() =
let preProcessedItem = getItem "Steve" 5
let postProcessedItem = processItem preProcessedItem

printItem "preProcessed" preProcessedItem
printItem "postProcessed" postProcessedItem

main()
```

This program processes an instance of the `TransactionItem` class and prints the results. This program outputs the following:

```preProcessed: {Name = "Steve";
ID = 5;
ProcessedText = null;
IsProcessed = false;}
postProcessed: {Name = "Steve";
ID = 5;
ProcessedText = "Done";
IsProcessed = true;}```

### Pattern Matching Records

We can pattern match on records just as easily as tuples:

```open System

type coords = { X : float; Y : float }

let getQuadrant = function
| { X = 0.0; Y = 0.0 } -> "Origin"
| item when item.X >= 0.0 && item.Y >= 0.0 -> "I"
| item when item.X <= 0.0 && item.Y >= 0.0 -> "II"
| item when item.X <= 0.0 && item.Y <= 0.0 -> "III"
| item when item.X >= 0.0 && item.Y <= 0.0 -> "IV"

let testCoords (x, y) =
let item = { X = x; Y = y }
printfn "(%f, %f) is in quadrant %s" x y (getQuadrant item)

let main() =
testCoords(0.0, 0.0)
testCoords(1.0, 1.0)
testCoords(-1.0, 1.0)
testCoords(-1.0, -1.0)
testCoords(1.0, -1.0)
Console.ReadKey(true) |> ignore

main()
```

Note that pattern cases are defined with the same syntax used to create a record (as shown in the first case), or using guards (as shown in the remaining cases). Unfortunately, programmers cannot use the clone syntax in pattern cases, so a case such as `| { item with X = 0 } -> "y-axis"` will not compile.

The program above outputs:

```(0.000000, 0.000000) is in quadrant Origin
(1.000000, 1.000000) is in quadrant I
(-1.000000, 1.000000) is in quadrant II
(-1.000000, -1.000000) is in quadrant III
(1.000000, -1.000000) is in quadrant IV```