Tuples are ok

Pragmatic Maciej - Mar 28 '20 - - Dev Community

There are opinions in the community that Tuple types should not be used ever. There are movements also against functions arguments, and using one dictionary/map argument instead. As with all radical opinions, saying that we should not use tuples is wrong. We should, but not for everything, in the same way there is no ideal data structure, tuple has limited correct scope of use.

What is Tuple

Tuple type represents ordered list of fixed size, and fixed type of elements. The most common tuple is a pair, so tuple with 2 elements. For example we can represent a point by a pair [number, number] in TS notation.

Functions arguments list is a tuple

If you have a function, its arguments form a tuple, so for example lets consider simple move function which will move the point.

// [TS]
declare function move(x: number, y: number): void
move(1,2);
// equivalent to
declare function move(...[x, y]: [number, number]): void
move(1,2)
Enter fullscreen mode Exit fullscreen mode

Tuple is isomorphic to Map

Tuple and Map/Dictionary are example of Product types and are isomorphic. Isomorphism means that from every tuple we can make a map, and from every map we can make a tuple. The proof is simple transformation in boths direction.

// [TS]
type TuplePoint = [number, number];
type MapPoint = {x: number, y: number};
// below transformations in both directions
function toTuple({x,y}: MapPoint) {
  return [x,y]
}
function toMap([x,y]: TuplePoint) {
  return {x, y}
}
Enter fullscreen mode Exit fullscreen mode

When to use Tuple

Tuples are great if small. It means that there is no issue in using double or triple tuples. The question starts at quadruple, for me it is a moment when it can work well, but also it can start to be a problem. But to be clear, I believe there can exists great use for longer tuples, however I would be careful with such.

Good examples of using tuples are points, dimensions like (x,y), (width, height), (x,y,z), also almost all pairs, like (name, lastname), (symbol, translation) and so on. Because of destructuring (destructuring exist in most languages with tuples - JS/TS, Python, Elm, Reason, Haskell) and possibility of naming elements of the tuple, there is also no issue in readability. Consider comparison function taking two arguments (a pair), and function taking one labelled argument.

// [TS]
function fullName(name, lastName) {
  return name.concat(lastName);
}
fullName("John", "Doe");
// in contrary version with map
function fullName({name, lastName}) {
  return name.concat(lastName);
}
fullName({name: "John", lastName: "Doe"}) // more boilerplate
Enter fullscreen mode Exit fullscreen mode

React useState as a great usage of the Tuple type

React hook useState is returning a tuple. The reason why tuple is the best choice here is the polymorphism of useState. We really use the same function to represent different states, so also the naming should be different. JS destructuring feature allows for local aliasing tuple structures.

// [JS]
const [name, setName] = useState("");
const [lastname, setLastName] = useState("");
const [age, setAge] = useState(0);
Enter fullscreen mode Exit fullscreen mode

In contrary how it would look if React team would use map instead:

// [JS]
const {value: name, setValue: setName} = useState("");
const {value: lastName, setValue: setLastName} = useState("");
const {value: age, setValue: setAge} = useState(0);
Enter fullscreen mode Exit fullscreen mode

Better? Don't think so 😉.

To be fair we could be doing with map const nameState = useState(""); nameState.setValue("Tom"); What is kinda ok.

When to not use tuples

As said before, tuples are great when small. Long tuples can be a pain, the biggest reason is that with longer tuple its harder to remember at which position stands which thing, we can fix that in TypeScript by aliasing types, but this is an additional thing to do. So I would rather think twice before using longer tuple.

The bad using of the tuple

// [TS]
type User = [string, string, number, bool]; // yhym, so what is second string?
// we can fix that by aliasing
type Name = string;
type Lastname = string;
type Age = string;
type Active = boolean;
type LittleBetterUser = [Name, LastName, Age, Active] // yhym now I get it
// but map will work best here
type UserAsMap = {
  name: string,
  lastname: string,
  age: number,
  active: boolean
}
Enter fullscreen mode Exit fullscreen mode

BTW, do you remember that functions with many arguments are consider as a bad practice? As we already said function arguments list is a tuple, and using long tuple can be a burden, in the same way functions with many arguments can be considered as a problem.

Summary

Tuple is a very nice structure, used wisely should be considered as alternative for small structs/maps. Don't be radical and give Tuple some love ❤.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player