1 of 22

Records and Tuples

2025 and beyond

2 of 22

Previously: 2024-04 "Discussing new directions"

typeof #{x: 10, y: 20} // "record"

typeof #[1, 2, 3] // "tuple"

#{x: 0, y: 20} === #{x: -0, y: 20}�

#[1, 2, 3] === #[1, 2, 3]

3 of 22

A design I really like

  • Has syntax: #{a: 1, b: #[2, 3]}
  • typeof is "object"
  • Can contain any values
  • Provides composite keys

4 of 22

Has Syntax

#{a: 1, b: #[2, 3]}vs f({a: 1, b: f([2, 3])})

  • ✅ Direct and reliable
    • No: const {freeze:f} = Object
    • Static guarantees for tooling
  • ✅ Ergonomic
    • Only 1 extra character to read and type
  • ✅ Direct allocation (*)

5 of 22

Has Syntax - static guarantees

export const config = #{

pages: #{

size: 10

}

};

import { config } from "./config.js";

get(config.pages.size); // 'guaranteed' 10

6 of 22

Has Syntax

  • Syntax is optional
  • Some ideas:
    • Object.isImmutable

    • Object.toImmutable
    • Object.toImmutableFromEntries

    • Object.immutable
    • Object.immutableFromEntries

    • Array.immutable

7 of 22

Are Objects

typeof #{} === "object"

Array.isArray(#[])

  • ✅ Backwards compatible with more existing code
    • Less disruptive to adopt
  • ✅ Not a fundamental new concept when learning the language
    • Can come in a later chapter

8 of 22

Shallow immutability

Object.isFrozen(#[])#[new PlainDate(2025,2,18), new PlainDate(2025,2,20)]

  • ✅ Can use value types appropriate for a domain
  • 🕵️‍♂️ Lint rules can still encourage literals to be nested immutably
  • 📦 Explicit Box exit points break compatibility with existing code
  • 🔴 Not a deeply immutable guarantee

9 of 22

Deep immutability alternative

R&T

SharedStructs

Symbols

JSON

acyclic

BigInt

undefined

writable

10 of 22

Deep immutability alternative

#{ __proto__: vector2DProto, x, y }

new PlainDate(2025,2,18).toRecord()

11 of 22

immutability

shallow

deep

flexibility

guarantees

12 of 22

Composite Keys�or how immutable data structures are wonderful for equality

13 of 22

Composite Keys

Map.groupBy(arr, obj => obj.x) ��Map.groupBy(arr, obj => [obj.x, obj.y]) 🔴�Map.groupBy(arr, obj => `${obj.x}:${obj.y}`) 🟠�

14 of 22

Composite Keys

Map.groupBy(arr, obj => obj.x) ��Map.groupBy(arr, obj => [obj.x, obj.y]) 🔴�Map.groupBy(arr, obj => `${obj.x}:${obj.y}`) 🟠�Map.groupBy(arr, obj => key(obj.x, obj.y)) 🟢

15 of 22

Composite Keys in the wild - interning

key(objA, objB, 1n, "abc") === key(objA, objB, 1n, "abc")

Weak K

V

objA

Weak K

V

objB

K

V

1n

K

V

"abc"

W<🔑>

FinalizationRegistry

16 of 22

Composite Keys in the wild - interning

  • Pros:
    • Existing capability
    • Works with existing equality semantics
  • Cons
    • Creation cost overheads
    • GC complexity
    • Our friend -0

17 of 22

Composite Keys

Map.groupBy(arr, obj => obj.x) ��Map.groupBy(arr, obj => [obj.x, obj.y]) 🔴�Map.groupBy(arr, obj => `${obj.x}:${obj.y}`) 🟠�Map.groupBy(arr, obj => key(obj.x, obj.y)) 🟢�Map.groupBy(arr, obj => #[obj.x, obj.y]) 👀

18 of 22

Composite Keys

  • Inert - no side-effects, noexcept
  • ✅ Reflexive
    • equals(x, x)
  • ✅ Symmetric
    • equals(a, b) === equals(b, a)
  • ✅ Transitive
    • (equals(a, b) && equals(b, c)) === equals(a, c)
  • ✅ Consistent
    • equals(a, b) === equals(a, b)

19 of 22

Composite Keys

  • Tagged with an internal slot [[isRecord]]
    • Object.isRecord(#[])
  • 🔄 Equality is recursive
    • Object.equalRecords(#{t: #[]}, #{t: #[]})
  • #️⃣ Can use hash codes internally
  • Objects can't become records after the fact
    • Consistent
    • (*) Syntax enables direct allocation

20 of 22

Composite Keys

  • ✅ No interning overhead
  • ✅ Out-of-the-box APIs
    • new Set([#{}, #{}]).size // 1
    • Map.groupBy(arr, obj => #[obj.x, obj.y])
    • tc39/proposal-iterator-unique
      • itr.uniqBy(obj => #[obj.x, obj.y])

21 of 22

Composite Keys

  • ⚖️ Language now has 5 forms of equality 🤯
    • Interning would keep at 4
    • Or completely replace 'SaveValueZero' 😱
  • 🛠️ Modifies existing APIs
    • More complexity for polyfills
    • +1 for new APIs: CompositeMap.groupBy
  • 🔴 Equality is only deep for the 'R&T' part of the tree
    • +1 for enforcing deep equality

22 of 22

Let's discuss