F Sharp Programming/Tuples and Records

Defining Tuples
A tuple is defined as a comma separated collection of values. For example,  is a 2-tuple with the type. 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.

This function has the type, it takes a   tuple and returns another.

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.

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

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

Every tuple has a property called arity, which is the number of arguments used to define a tuple. For example, an  tuple is made up of two parts, so it has an arity of 2, a   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  that prints out a custom greeting based on the specified name and/or language.

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

Example 2

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

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

They have the following types:

Here are a few examples in FSI:

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:

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

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

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:

Some methods, such as the  shown above, and others such as   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:

Defining Records
A record is similar to a tuple, except it contains named fields. A record is defined using the syntax:
 * means the element must occur one or more times.

Here's a simple record:

Unlike a tuple, a record is explicitly defined as its own type using the  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  record is created by specifying the record's fields as follows:

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  first and   second, it's perfectly legitimate to write:

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

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:

The method  has the type. The  keyword creates a clone of   and set its   property to.

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

Here's a more complete program:

This program processes an instance of the  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: 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  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