Tuple Tuesday!

.NET Team

.NET Team

One of the central features we’re trying to add to both VB and C# in the near future is tuples. If you’ve never heard of a tuple it’s a primitive data structure for combining values together that many other programming languages have supported for years, including .NET’s F#, but has been suspiciously absent from Visual Basic. A tuple is vaguely like an array in that it’s a data structure that bundles up multiple values together as a unit, except whereas in an array each element of the array has the same type, in a tuple every element may have a different type. So, for example, if I found in my program that I was always passing around two values together I could combine them into a tuple:

Without tuples…

Sub Method1(x As Integer, y As Integer)     ‘ Do something.     Method2(x, y) End Sub

Sub Method2(x As Integer, y As Integer)     ‘ Do other stuff. End Sub

With tuples…

Sub Method1(point As Tuple(Of Integer, Integer))     ‘ Do something.     Method2(point) End Sub 

Sub Method2(point As (Integer, Integer))     ‘ Do other stuff. End Sub

In the above example instead of passing two integer parameters around separately from method to method I can instead pass a tuple which contains the two integers. This makes it clear that these values are to be handled as a unit. In this example I used the generic System.Tuple(Of T1, T2) type, which exists in .NET 4.0 an up. You can do this today but there are two downsides. First, System.Tuple(Of T1, T2) and its related types are all classes so every time you create one you’re allocating a new object on the heap. With VB and C# we’re thinking of using a new set of value-type tuple types like ValueType(Of T1, T2) that are allocated inline so you aren’t penalized for using this convenient feature. Second, after receiving a lot of feedback from customers about how important they were, the built-in VB (and C#) tuples will have names:

Sub Method1(point As (X As Integer, Y As Integer))     ‘ Do something.     Console.WriteLine($”X={point.X}, Y={point.Y}) End Sub

This means you get all the self-documenting names of declaring your own structure but with a lightweight syntax. This is similar to the anonymous types VB has already but with the benefit that you can pass them around outside of methods. This makes VB tuples half-way between what languages like F# call a tuple and what they call a record. We think this is the sweet spot.

But while neat, just passing data around in a bundle isn’t all that compelling. The rockstar scenario for tuples is returning multiple values. Without tuples you’d write a method like this, which divides two numbers and returns the quotient and the remainder, this way:

”’ <Summary>Divides two integers and returns the quotient and remainder.</Summary> Function DivRem(dividend As Integer, divisor As Integer, ByRef remainder As Integer) As Integer     remainder = dividend Mod divisor     Return dividend \ divisor End Function

Because VB functions have always returned exactly one value you had to return additional values using ByRef variables passed in by the caller. Or, if you really wanted to you could declare a QuotientAndRemainder structure in your code but honestly who wants to have a whole type with a goofy name like that in their project just for one method to return it? With tuples you don’t have to make these tradeoffs:

”’ <Summary>Divides two integers and returns the quotient and remainder.</Summary> Function DivRem(dividend As Integer, divisor As Integer) As (divisor As Integer, remainder As Integer)     Return (dividend \ divisor, dividend Mod divisor) End Function

As a language that aims to be readable and approachable I think this is by far the easiest and most straight-forward way to return multiple values from a function. We think tuples will be very helpful with reducing a lot of the ceremony and internal bookkeeping around returning and passing data within your project by making bundling data as easy as possible. And when you’re using the powerful Async feature this really shines because in Async functions you can’t even have ByRef parameters at all, so you can’t use the old strategy. Today you’d need to declare a special structure, or use an array (if all the values have the same type), or use the Tuple type and lose the meaningful names. With tuples you don’t have to give up any of the convenience:

”’ <Summary>Asynchronously enumerates all orders in the database for the month and calculates the total value of the orders, the average order value, and total number of orders.</Summary> Async Function CalculateMonthlyTotalsAsync() As Task(Of (total As Decimal, average As Decimal, count As Integer))

This method does some fetching of data over the network from the database so it should be Async and runs over all the orders computing useful stats like the total value of all the orders, average order, and number of orders. Without tuples you’d have to add a type to your project called TotalAverageAndCount—yuck!

You can read the full set of design decisions about tuples in Visual Basic so far by reading the design notes on GitHub. Including some of the other ways tuples will integrate with the rest of the language. Please leave your feedback on the feature design on GitHub and/or below in the comments.

I was wracking my brain for days last week trying to come up with a good pun for introducing tuples, which is why this post was a week later than planned. “Tuple Tuesday” finally hit me over the weekend. To avoid mix-ups like that in the future the VB language design team is aggressively brainstorming dozens of puns for other features we’re looking to add. I think the next design topic will be “Visual Basic and Patterns – A Match Made In Heaven!” (which I think will add an entirely new dimension to VB) so stay tuned. Until then, happy coding!




Comments are closed. Login to edit/delete your existing comments