Bookmark and Share

Unpack and Transform Tuples with CPS

by KodefuGuru 28. July 2010 17:13

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
C# MVP, INETA Community Champion


MVP - Visual C#

 

INETA Community Champions
Friend of RedGate
Telerik .NET Ninja
Community blogs & blog posts

I am a #52er

I have joined Anti-IF Campaign


World Map

Tag cloud

Disclaimer

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

© Copyright 2010