F# Tuples

How often have you had a few data elements that you wanted to keep together in an ordered manner without going through the trouble of creating a custom type because they were only going to be used in one or two places?  If the data was guaranteed to always be the same type an array or other collection type might suffice but we really have no guarantee about its size and we generally want the data contained in one object.

Most of the time I find myself in this situation, I find that I want something more than an array.  Sometimes that means I end up abusing KeyValuePair<TKey, TValue> for simple pairs or resorting to creating a class or struct that has no use outside of one small area.

.NET 3.0 introduced anonymous types but those are only useful in a few situations without jumping through a bunch of coding hoops.  .NET 4 gave the traditional .NET developer some relief by introducing the generic, immutable Tuple classes that are intended to address this very issue. Unfortunately, using them in a traditional .NET language isn’t particularly convenient.

Tuples in C#

Using Tuples in a language like C# is pretty cumbersome to say the least. To Microsoft’s credit, they provided an overloaded factory method to make them a bit easier to create but it doesn’t address their verbosity in other areas. Consider a function that calculates some measurements for a circle:

Tuple<double, double, double> GetCircleMeasurements(double radius)
{
  var diameter = radius * 2.0;
  var area = Math.PI * Math.Pow(radius, 2.0);
  var circumference = 2.0 * Math.PI * radius;

  return Tuple.Create(diameter, area, circumference);
}

Aliasing

The Create method saves us from having to type full tuple definition when creating one but we need to be more explicit virtually everywhere else. Depending on the number of members in a tuple I sometimes find it helpful to alias the type with a meaningful name. It’s still a bit verbose but to me it’s a good balance of leveraging an existing type while reducing potential points of failure. If we had another Tuple in the same context (say, another method that wanted to do something with the values) and we wanted to change the tuple elements we’d only need to change the alias to affect each place.

// With other using directives
using CircleMeasurements = System.Tuple<double, double, double>;

CircleMeasurements GetCircleMeasurements(double radius)
{
  var diameter = radius * 2.0;
  var area = Math.PI * Math.Pow(radius, 2.0);
  var circumference = 2.0 * Math.PI * radius;

  return new CircleMeasurements(diameter, area, circumference);
}

Accessing Tuple Members

Accessing tuple items in C# is pretty straightforward; we simply access each item via a property:

var measurements = GetCircleMeasurements(2.5);
var diameter = measurements.Item1;
var area = measurements.Item2;
var circumference = measurements.Item3;

Tuples in F#

Given that this is actually a post about tuples in F# you’ve probably guessed by now that using tuples is much simpler in F#. Not only do we get the benefits of type inference, F# has a built-in tuple syntax! In F# tuples are represented by a comma-delimited list and generally enclosed in parenthesis.

Here is the first example above reproduced in F#:

> let GetCircleMeasurements radius =
	let diameter = radius * 2.0
	let area = Math.PI * (radius ** 2.0)
	let circumference = 2.0 * Math.PI * radius

	(diameter, area, circumference)
	
val GetCircleMeasurements : float -> float * float * float

Don’t forget an open System;; line if trying this through FSI.

Although the bulk of the function body is the same you can see that we don’t have to concern ourselves with anything related to the framework itself – we simply use a language construct.

Representing Tuples

Tuples can be thought of as the product of two or more data types so they are represented by delimiting the items with the * character. For example, the tuple returned by the GetCircleMeasurements function would be represented as float * float * float.

Accessing Tuple Members

Accessing tuple members in F# is generally also be significantly easier than in C#. F# provides two built-in functions, fst and snd, for accessing the first and second items in a pair ('a * 'b). An error will be generated if you try to use these functions with a tuple containing more than two items so we something else for our example.

A primary way we can extract values from a tuple is through let bindings. We can easily extract all three values from the tuple returned by GetCircleMeasurements into separate variables. Compare this to the C# example above:

> let (diameter, area, circumference) = GetCircleMeasurements 2.5;;

val diameter : float = 5.0
val circumference : float = 15.70796327
val area : float = 19.63495408

The number of names defined in the binding must match the number of values in the tuple. If they don’t match we’ll get an error. If we only care about some of the values we can simply replace the names we don’t care about with underscores. For instance, if we only care about the area we could write this:

> let _, area, _ = GetCircleMeasurements 2.5;;

val area : float = 19.63495408

We can even easily define a function to return the third value from a triple (tuple with three items):

> let third (_, _, c) = c;;

val third : 'a * 'b * 'c -> 'c

> let circumference = third (GetCircleMeasurements 2.5);;

val circumference : float = 15.70796327

Language Interoperability

If you intend to use tuples across languages it’s important to keep in mind which version of the language you’re using. The tuple classes described in the C# section above were originally located in FSharp.Core.dll. When targeting a framework version prior to 4.0 the compiler uses the versions from this assembly rather than the ones in mscorlib.

Tuples also play a role when calling functions from other languages. Within F# most functions are curried (calling a function with fewer than the specified number of arguments results in creating a new function that accepts the remaining arguments) but outside of F# is a different story. Calling functions from other languages requires what is referred to as a syntactic tuple. Syntactic tuples look like the tuple types but as their name implies, they’re merely a mechanism of the syntax.

Next Steps

Tuples are convenient constructs that are available throughout the .NET Framework as of v4.0 yet cumbersome to use outside of F#. F#’s built-in tuple support makes using them virtually effortless. Despite their convenience, sometimes tuples aren’t enough. What if we want to name the values or extend functionality with custom functions? In those cases we can turn to the next topic in this series – an F# construct called a record type.

Advertisement

One comment

Comments are closed.