Destructuring of Tuples

Topics: C# Language Design
Jul 9 at 2:50 PM
Note that this discussion is different from https://roslyn.codeplex.com/discussions/549742, in that the mentioned discussion talks about destructing arrays.
Destructing of tuples would be a really nice feature to have in C#. Having to do t.Item1 and t.Item2 get's tedious after a while, and having it built into the language would be very appreciated.

It also allows for easy swapping using (a, b) = (b, a).
Jul 9 at 9:08 PM
You've shown an example of swapping. What would the other uses look like?
Jul 10 at 2:27 AM
Alxandr wrote:
Note that this discussion is different from https://roslyn.codeplex.com/discussions/549742, in that the mentioned discussion talks about destructing arrays.
Destructing of tuples would be a really nice feature to have in C#. Having to do t.Item1 and t.Item2 get's tedious after a while, and having it built into the language would be very appreciated.

It also allows for easy swapping using (a, b) = (b, a).
I'd like to see this and better support for tuples in general. Working with Tuple directly is just obnoxious and the language could do more to bridge the gap between that type and anonymous types.

The syntax below is strictly for illustrative purposes.
var tuple = { 404, "Not Found" }; // syntax candy for Tuple.Create<int, string>(404, "Not Found")

int errorNumber = tuple.1; // syntax candy for tuple.Item1
string errorDescription = tuple.2; // syntax candy for tuple.Item2

// alternatively
var { errorNumber, errorDescription } = tuple; // deconstructs the Tuple<int, string> into two variables of type int and string
// declare a function that returns Tuple<int, string>
public { int, string } GetHttpError()
{
    return { 404, "Not Found" };
}

// declare a function that returns an anonymous type, which is lifted to the scope of the declaring type
public { int errorNumber, string errorDescription } GetHttpErrorNamed()
{
    return { 404, "Not Found" };  // shorthand for new { errorNumber = 404, errorDescription = "Not Found" };
}

Jul 10 at 7:38 AM
@Halo_Four I don't think having named return-values is a good idea, however returning tuples sure is.

@JesperTreetop Let me take an example of code I'm working on at the moment. I have a method GetRequests that returns a list of request (paged), as well as an out-value containing the total number of request, signature like this: IEnumerable<Request> GetRequests(int page, out int totalCount);.

For logging-purposes I have a method that takes a method name, and an lambda, and logs entry and exits (and faults) happening in said method. The logging method looks like this:
public static T Function<T>(this ILog log, Func<T> func, string nameFormat, params object[] args)
{
    log.TraceFormat("Start: " + nameFormat, args);
    try
    {
        return func();
    }
    catch (Exception e)
    {
        log.WarnFormat("Fault: " + nameFormat, args);
        log.Warn("Exception: ", e);
        throw;
    }
    finally
    {
        log.TraceFormat("End: " + nameFormat, args);
    }
}
This is a pretty straight forward function, and it works great for most purposes, however a problem arises when you
have an out-parameter on the function using it, cause I can't no longer just do return log.Function(.....). So I end up with
this code:
public IEnumerable<Request> GetRequests(int page, out int totalCount)
{
    var ret = Log.Function(() =>
    {
        using (var conn = new IngresConnection(_connectionString))
        {
            conn.Open();
            var tc = GetMessageCount(conn, status);
            var messages = GetMessages(conn, status, page: page);
            Log.DebugFormat("{0} messages found", messages.Count);
            return Tuple.Create(messages, tc);
        }
    }, "GetRequests");

    totalCount = ret.Item2;
    return ret.Item1;
}
As you can see, this is not ideal. If however I had destructing (and constructing) of tuples baked into the language I could change it to this:
public IEnumerable<Request> GetRequests(int page, out int totalCount)
{
    IEnumerable<Request> ret;
    (ret, totalCount) = Log.Function(() =>
    {
        using (var conn = new IngresConnection(_connectionString))
        {
            conn.Open();
            var tc = GetMessageCount(conn, status);
            var messages = GetMessages(conn, status, page: page);
            Log.DebugFormat("{0} messages found", messages.Count);
            return (messages, tc);
        }
    }, "GetRequests");

    return ret;
}
Jul 10 at 8:27 PM
I would not recommend that a language do too much to encourage the use of tuples unless support is added for tuple structures, which should probably support implicit conversion to and from a tuple class type. For tuples of any size, structures will outperform class objects unless the tuple is passed around at least twice, and the only time using a class will substantially outperform a structure is in cases where a structure would end up getting boxed repeatedly. That possibility is probably sufficient to justify using class objects for tuples instead of structures, but certainly for the scenario where a tuple returned by a method would get immediately decomposed by the caller and never used again a structure would be more appropriate, regardless of the number of members.

Otherwise, I certainly like the idea of being able to use a syntax like {var1, var2} = methodReturningTupleStructure();. Such a thing could be especially efficient if TupleStruct<T1,T2> had public fields, since given:
int foo,bar;
{foo,bar} = GetXY();
the compiler could rewrite the code as:
TupleStruct<T1,T2> tup;
tup = GetXY();
and then replace all references to foo with tup.Item1 and all references to bar with tup.Item2. This would in many cases avoid an extra set of copying operations that would be required if the code were written (by either a human or the compiler) as:
int foo,bar;
TupleStruct<T1,T2> tup;
tup = GetXY();
foo = tup.Item1;
bar = tup.Item2;
In some cases, the latter style of code would be unavoidable (e.g. given
if (someCondition)
  {foo, bar} = someFunction();
else
  {bar, foo} = someFunction();
one of the assignments could use the faster form, but the other would have to manually copy the items from the return value to the variables.
Jul 11 at 1:35 PM
F# already uses the Tuple type quite extensively if I'm not mistaken. And sure, there's some extra copy-instructions, but don't you think they probably get optimized away somewhere?
Aug 7 at 8:54 PM
Yep, in the post linked in the first message, I proposed tuples destructuring as well.
Although I'd love to see both arrays and tuples destructuring, I have to say I use tuples quite seldom.
Perhaps, that is due to the annoying need to type Item1 (which I think makes the code a bit ugly).
Aug 9 at 12:31 PM
Edited Aug 9 at 11:30 PM
By the way, Mono has already got support for tuple deconstruction since 2009. So at least this feature doesn't seem to lead to parsing problems.

Perhaps it's time to catch up? If C# is moving towards more functional style, syntactic support of multiple return values would be really a good feature. (I think exactly the absence of such a support is the main reason why tuples remain a rather exotic feature.)

Another idea I think of would be automagic conversion of out parameters, so that the users can take advantage of the same syntax. Example:
int FindNearestItem(IList<double> haystack, double needle, out double found) { ... }

// old syntax:
double realValue;
var index = FindNearestItem(list, 0, out realValue);

// proposed syntax:
(var index, var realValue) = FindNearestItem(list, 0, out /*omitted*/);
This way there is no syntactic difference between a function with out parameters and tuple-returning function: after all, semantically they both return multiple values.
Aug 23 at 1:28 AM
Edited Aug 23 at 1:49 AM
I think there is already one library-based solution for destructuring tuples, albeit an imperfect one. All that is needed is some destructuring methods in each of the Tuple types. For example for the 3-Tuple we can have:
public class Tuple<T1, T2, T3>
{
    private readonly T1 m_Item1;
    private readonly T2 m_Item2;
    private readonly T3 m_Item3;

    public T1 Item1 { get { return m_Item1; } }
    public T2 Item2 { get { return m_Item2; } }
    public T3 Item3 { get { return m_Item3; } }

    public Tuple(T1 item1, T2 item2, T3 item3)
    {
        m_Item1 = item1;
        m_Item2 = item2;
        m_Item3 = item3;
    }

    public void Apply(Action<T1, T2, T3> action)
    {
        action(m_Item1, m_Item2, m_Item3);
    }

    public TX Select<TX>(Func<T1, T2, T3, TX> func)
    {
        return func(m_Item1, m_Item2, m_Item3);
    }

    public void GetItems(out T1 item1, out T2 item2, out T3 item3)
    {
        item1 = m_Item1;
        item2 = m_Item2;
        item3 = m_Item3;
    }
}
Having defined Apply, Select and GetItems, one can write:
var tuple = new Tuple<string, string, int>("John", "Doe", 32);

tuple.Apply((firstName, lastName, age) =>
{
    Console.WriteLine("{0} {1} is {2} years old.", firstName, lastName, age);
});

// in functional style:
string fullName = tuple.Select((firstName, lastName, age) => string.Format("{0} {1}", firstName, lastName));

// or if you prefer a more imperative style, you can use the new "declaration statement":
tuple.GetItems(out string firstName, out string lastName, out int age);
...
By the way, nothing prevents these methods from being defined as extension methods, so you can experiment with them right away.