Tuple abuse
2021-11-03 Permalink
Tuple types tend to be heavily misused. The anonymity of their heterogeneous members obfuscates the code in places where a simple struct with named fields would be more appropriate.
Let’s walk through a few examples from the C++ standard library and elsewhere (though the discussion is not limited to C++, as I’ve seen Go, Lua and Python code abusing them just as well).
- Associative containers (like - std::map) define their- value_typeas:[1]- typedef std::pair<const Key, T> value_type; - The key and value of an element of the container are thus given the uninformative names - firstand- second. Using a- structin this context would improve the readability:[2]- struct value_type { const Key key; T value; };
- Unique associative containers also have this insert function:std::pair<iterator,bool> insert(const value_type &value); 15 years in, and I still don’t remember which one is firstand which one issecond; nor if thatboolindicates success or failure. This is another case where a littlestructwould make a huge difference:struct insert_result_type { iterator where; bool inserted; }; insert_result_type insert(const value_type &value);
- equal_rangeof all kinds returns a- pair:- std::pair<Iter,Iter> equal_range(...); - Instead it should have returned an - iterator_range:- template<class Iter> struct iterator_range { Iter first, last; Iter begin() const { return first; } Iter end() const { return last; } // ... };
- Some code uses tuplesfor no justification other than the laziness of the programmer:std::tuple<int,double,double> stats(); Is that a count, mean and variance? Or a count, a minimum and a maximum? Or...? 
Just like function parameters have names, so should their return types, or any other aggregate types for that matter. I thus conclude that given a choice between a struct and a tuple, the former should be preferred in the vast majority of circumstances.
Tuples are, in their essence, structs with unnamed fields. In a Fantasy Language™ these two would be conflated.
Footnotes
- std::pairin C++ is an old special case of an- std::tuple, thus the discussion here applies to it just as well.
- Perhaps - valueshould be renamed to- mapped. However I’d say that- setand- mapshould have been combined by removing the- valuepart altogether, and extracting a key from a user provided- value_type.
