Bookmark and Share

Unpack and Transform Tuples with CPS

The tuple class made its debut in the .NET framework along with a language that supports it well: F#. But being a first-class citizen of the framework means other languages have access to this data type as well. Unfortunately, neither C# nor VB obtained any language support for tuples, and it ends up being treated as a dumb, generic data container instead of the powerful data class that is oftentimes found in functional languages.

Accessing the elements of a tuple is rather bland, as each piece is named sequentially Item1, Item2, and so on. Compare this to an anonymous type, essentially the same sort of class, where each element has been given a name. The only way to unpack a tuple in C# is to assign each item property to another variable or object property. Of course, this leads to rote assignments which junks up code.

I have no way to modify the compiler to act more like F#. However, there are some techniques we can use to make working with tuples a little easier in C#. Extension methods allow us to assign functionality to a tuple. In this case we want tuples to be able to unpack and transform themselves. With continuation-passing style, the tuple can pass its values to a lambda that we define, thereby eliminating the ugly ItemX syntax.

Each extension method will require overloads for each generic version of Tuple. For the examples, I will be demonstrating the triple.

public static void Unpack<T1, T2, T3>(this Tuple<T1, T2, T3> tuple, Action<T1, T2, T3> action)
{
    action(tuple.Item1, tuple.Item2, tuple.Item3);
}

Unpack allows the caller to assign the items of the tuple to other variables or properties of an object. Let’s take a look at the unit test.

[TestMethod]
public void Unpack()
{
    var tuple = Tuple.Create(1, 2, 3);
    int x = 0;
    int y = 0;
    int z = 0;

    tuple.Unpack((a, b, c) => { x = a; y = b; z = c; });

    Assert.AreEqual(1, x);
    Assert.AreEqual(2, y);
    Assert.AreEqual(3, z);
}

Item1 becomes a, Item2 becomes b, and Item3 becomes c. We then take our named elements and unpack them into the variables we desire. There is no concept of no match; if you don’t want to unpack one of the variables, don’t assign it to something. You could use the _ one time to indicate non-usage as _ is a valid variable name in C#. E.g. tuple.Unpack((a, _, c) => { x = a; z = c; });.

What if we want to do more than unpack… say transform the tuple into another type?

public static TResult Transform<T1, T2, T3, TResult>(this Tuple<T1, T2, T3> tuple, 
Func<T1, T2, T3, TResult> func) { return func(tuple.Item1, tuple.Item2, tuple.Item3); }

Here’s the test to show how it works.

[TestMethod]
public void Transform()
{
    var tuple = Tuple.Create(3, 4, 5);
    var triangle = tuple.Transform((a, b, c) => new {Width = a, Height = b, Hypotenuse = c});
    
    Assert.AreEqual(3, triangle.Width);
    Assert.AreEqual(4, triangle.Height);
    Assert.AreEqual(5, triangle.Hypotenuse);
}

This is very similar to Unpack, but it returns an object instead. This allows you to create a new object, or assign properties on an existing object and continue a method chain.

I hope continuation-passing style helps you out if you must work with tuples in C#. Of course, if you have heavy processing of tuples, it may be time to switch to a language that handles them better... at least for that particular project.

blog comments powered by Disqus

KodefuGuru.GetInfo()

Chris Eargle
LinkedIn Twitter Technorati Facebook

Chris Eargle
Telerik Developer Evangelist, C# MVP

JustCode

Telerik .NET Ninja

 

INETA Community Speakers Program

 

MVP - Visual C#

 

Friend of RedGate

World Map

Tag cloud

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2010
Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.