Why use tuples instead of objects?

In the code base where I work, there is an object called Pair, where A and B are the types of the first and second values โ€‹โ€‹in Pair. I believe this object is offensive because it is used instead of an object with clearly defined members. Therefore, I find this:

List<Pair<Integer, Integer>> productIds = blah(); // snip many lines and method calls void doSomething(Pair<Integer, Integer> id) { Integer productId = id.first(); Integer quantity = id.second(); } 

Instead

 class ProductsOrdered { int productId; int quantityOrdered; // accessor methods, etc } List<ProductsOrderded> productsOrdered = blah(); 

Many other uses of the pair in the code base also smell bad.

I googled tuples, and they seem to be often misunderstood or used in questionable ways. Are there any convincing arguments for or against their use? I can appreciate not wanting to create huge class hierarchies, but are there realistic code bases where the class hierarchy explodes if tuples are not used?

+42
c ++ tuples
Mar 23 '09 at 21:08
source share
10 answers

First of all, a tuple is quick and easy: instead of writing a class for every time you want to combine 2 things, there is a template that does this for you.

Secondly, they are general in nature. For example, in C ++ std :: map uses std :: a pair of keys and values. Thus, you can use any pair, instead of creating some kind of wrapper class using access methods for each permutation of two types.

Finally, they are useful for returning multiple values. There really is no reason to make a class specifically for a function multiple return values, and they should not be considered as a single object if they are not connected.

To be fair, the code you inserted uses the pair poorly.

+35
Mar 23 '09 at 21:11
source share

Tuples are used all the time in Python, where they are integrated into the language and very useful (they allow multiple return values โ€‹โ€‹for starters).

Sometimes you just need to put things together and create a real, honest god, brute force class. Using tuples, on the other hand, when you really have to use a class, is just as bad as the reverse.

+16
Mar 23 '09 at 21:13
source share

The sample code has several different odors:

  • Rethinking Wheels

The structure already has a tuple; KeyValuePair structure. This is used by the Dictionary class to store pairs, but you can use it wherever it fits. (Not to say that it fits in this case ...)

  • Making a square wheel

If you have a list of pairs, it is better to use the KeyValuePair structure than a class for the same purpose, since this leads to less memory allocation.

  • Concealment of intention

The class with properties clearly shows what the values โ€‹โ€‹mean, and the Pair<int,int> class does not tell you anything about what the values โ€‹โ€‹represent (only they are probably related in some way). To make the code reasonably clear with such a list, you would need to give the list a very desciptive name, for example productIdAndQuantityPairs ...

+10
Mar 23 '09 at 21:48
source share

For what it's worth, the code in the OP is not a mess because it uses tuples, but because the values โ€‹โ€‹in the tuple are too weakly typed. Compare the following:

 List<Pair<Integer, Integer>> products_weak = blah1(); List<Pair<Product, Integer>> products_strong = blah2(); 

I would also be upset if my development team walked around identifiers, not class instances, because an integer can represent anything .




With that said, tuples are extremely useful if you use them correctly:

  • Correlations exist to group special values โ€‹โ€‹together. They are certainly better than creating an excessive amount of wrapper classes.
  • A useful alternative to out / ref parameters when you need to return more than one value from a function.

However, tuples in C # make my eyes water. Many languages, such as OCaml, Python, Haskell, F #, etc., have special, concise syntax for defining tuples. For example, in F #, a module defines a constructor as follows:

 val of_list : ('key * 'a) list -> Map<'key,'a> 

I can create an instance of the map using:

 (* val values : (int * string) list *) let values = [1, "US"; 2, "Canada"; 3, "UK"; 4, "Australia"; 5, "Slovenia"] (* val dict : Map<int, string> *) let dict = Map.of_list values 

The same code in C # is ridiculous:

 var values = new Tuple<int, string>[] { new Tuple<int, string>(1, "US"), new Tuple<int, string>(2, "Canada"), new Tuple<int, string>(3, "UK"), new Tuple<int, string>(4, "Australia"), new Tuple<int, string>(5, "Slovenia") } var dict = new Dictionary<int, string>(values); 

I do not believe that there is anything wrong with tuples, but the C # syntax is too cumbersome to make the most of them.

+8
Mar 23 '09 at 21:33
source share

Reuse of code. Instead of writing another class with exactly the same structure as the last 5 classes, similar to the dulls we created, you make ... a Tuple class and use it whenever you need a tuple.

If the only class value is โ€œstoring a pair of values,โ€ I would say that using tuples is an obvious idea. I would say that it was the smell of code (how much I hate this term) if you started to implement several identical classes so that you could rename two members.

+4
Mar 23 '09 at 21:49
source share

This is just the prototype code, which was probably broken together and was never reorganized. Do not fix it just laziness.

The actual use of tuples is universal functionality that does not really care about what constituent parts are, but just works at the tuple level.

+2
Mar 23 '09 at 21:11
source share

Scala has tuple-valued types, completely from two tuples (pairs) to tuples with 20+ elements. See First Steps to Scala (Step 9):

 val pair = (99, "Luftballons") println(pair._1) println(pair._2) 

Tuples are useful if you need to combine values โ€‹โ€‹for some relatively special purposes. For example, if you have a function that should return two fairly unrelated objects, instead of creating a new class to hold two objects, you return a couple from the function.

I completely agree with other posters that tuples may be misused. If the tuple has any semantics important to your application, you should use the correct class instead.

+2
Dec 08 '09 at 21:23
source share

An obvious example is a coordinate pair (or triple). Labels do not matter; the use of X and Y (and Z) is simply a convention. The formation of their homogeneity makes it clear that they can be treated equally.

+1
Mar 23 '09 at 21:26
source share

Many things have already been mentioned, but I think it should also be mentioned that there are some programming styles that are different from OOP and are very useful for these tuples.

Functional programming languages โ€‹โ€‹such as Haskell, for example, have no classes at all.

+1
Mar 23 '09 at 21:35
source share

If you are doing a Schwartz transform) to sort by a specific key (which is expensive to compute many times) or something like that, the class seems a little redundant:

 val transformed = data map {x => (x.expensiveOperation, x)} val sortedTransformed = transformed sort {(x, y) => x._1 < y._1} val sorted = sortedTransformed map {case (_, x) => x} 

Having a class DataAndKey or something like a little redundant here.

Your example was not a good example of a tuple, but I agree.

0
Mar 23 '09 at 22:39
source share



All Articles