Records and Tupples in JavaScript

A record and a tuple are the new primitives (a stage 2 proposal), which were designed to be very similar to objects and arrays respectively but to be different in one crucial aspect. That is being deeply immutable.

Being immutable is of course, means that you can't change its value. All you can do is create a copy.

Being deeply immutable is an even bigger deal. Since we are guaranteed that all the nested structures are immutable, we can compare deeply-nested structures simply with === same way as we compare primitives. In fact, the proposal's authors label them the new (compound) primitives.

Deeply immutable primitives would allow for simpler code but also would enable the JS engines to perform optimizations on construction, manipulation, and comparison of those primitives.

How to create a record/tuple

Here's how you create a record (or a tuple):

const user = #{
  id: 123,
  name: "John Silver",
  // there's a tuple!
  likes: #['cats', 'dogs', 'hamsters']
}

Yes, they are just objects / arrays but with prepended #.

Note that you can't mix them with mutable values.

const users = #[
  { name: "John" }
]

//=> TypeError: Tuple may only contain primitive values

How to work with records/tuples

Records and tuples, in many cases, behave as regular objects and arrays.

// accessing properties
user.id //=> 123
user.likes[1] //=> "dogs"

// Object.map works
Object.map(#{ x: 1, y: 2 }) //=> ['x', 'y']

// Spread works
const newTuple = #[ ...oldTuple, 42 ]

// map and filter is there
users
  .filter(u => u.name.startsWith("John"))
  .map(u => u.id)

// Tuples are iterable
for (const u of users) { console.log(u.name); }

// They even JSON.stringify
JSON.stringify(#{ a: #[1, 2, 3] }); // '{"a":[1,2,3]}'

// etc...

In general, you use the familiar APIs.

Then there are some things you can't do with records and tuples because they don't mutate.

cont user = #{name: "John"}
user.name = "Jane"
//=> TypeError: Cannot assign to read only property 'name' 

// same for tuple
const tuple = #[1, 2, 3]
tuple[0] = 5
//=> TypeError: Cannot assign to read only property '0' 

// mutating functions are not there
tuple.push(4)
//=> TypeError: tuple.push is not a function

Comparison

Since records and tuples are guaranteed to be immutable, they can be (deeply) compared by value.

const user1 = #{
  id: 1,
  name: 'John',
  likes: #['cats', 'dogs']
}

const user2 = #{
  id: 2,
  name: 'John',
  likes: #['cats', 'dogs']
}

// yep, the same user
user1 === user2 // true

Note the struck contrast with regular objects/arrays where you'd have to compare them either recursively or using weird hacks like converting to JSON.

The authors give a pretty good summary in the proposal text.

Where to go next?

🔥 100+ questions with answers
🔥 50+ exercises with solutions
🔥 ECMAScript 2023
🔥 PDF & ePUB