Racket’s data structures have distinct characteristics that often recommend one over another for a particular job: + Chart adapted from Jens Axel Søgaard.
|association list||sequential or random||variable||car||no|
Hash tables, vectors, and structure types are available in (meaning, internal values can be changed) and variants. As a rule, unless you need a mutable structure, choose an immutable one, because Racket can better optimize immutable structures for speed.
A list holds any number of values. Because lists are made from a sequence of linked pairs, they’re optimized for sequential access, and thus are the characteristic data structures used in iteration and recursion. Random access (with list-ref) is feasible with short lists. For long lists, random access can be slow, so a vector is the better option.
A vector is an array of values, indexed by location and optimized for random access. Though vectors come in immutable and mutable variants, the length of a vector is always fixed. + But see growable vectors, which can be resized.
A hash table is a mapping of keys to values, which can themselves be any data type. Hash values can be retrieved with hash-ref:
1 2 3 4 5 6 7 8
A hash table can be —meaning, values can be added, deleted, or updated—or . An immutable hash table is created with hash (as demonstrated above). A mutable hash table is created with make-hash, and then can be changed with hash-set!:
By default, a hash table uses equal? to compare keys. Hash tables that use the faster equality functions (eq? and eqv?) are also available. If your keys are only symbols, you can use hasheq or make-hasheq; if your keys are only symbols or numbers, you can use hasheqv or make-hasheqv.
A vector is faster than a hash table, so if you can use sequential integers for keys, a vector is the better choice.
Association lists are easy to make and break, with the help of map:
1 2 3 4 5 6
Association lists also can be used in a hash-table-ish way with generic functions like dict-ref:
A structure type is a user-defined data structure with an arbitrary number of fields. Like a hash table, a structure type allows access to its fields by name. Like a vector, the number of fields is fixed by the structure-type definition.
The struct form defines the structure type itself, and also manufactures a , plus (and, for mutable structure types, ) for each field. The #:transparent option makes the structure type printable in the REPL:
1 2 3 4 5 6 7
It’s not uncommon for a Racketeer to throw some values into a list as a quick-and-dirty data structure. But for anything that will be used more than once, a structure type is better:
The getters and setters have readable names.
The structure type can have fields added to its definition without changing existing references.
An instance of a structure type is represented as a single object in memory, so it can be passed around a program “by reference” (meaning, it’s not duplicated if passed from one function to another).
A structure type has its own unique predicate, which can be efficiently tested (say, inside a contract).
The disadvantage of a structure type is that it’s a distinct data type from a hash table, vector, or list, so it can’t be directly used with library functions that consume those types. There’s a little more housekeeping to get values in & out.
Classes—in the object-oriented programming (OOP) sense—are a big deal in many languages. But not in Racket.
Racket has them, of course. They’re used in a couple parts of the library (most prominently, the GUI framework). Otherwise, rarely. In DSLs, almost never.
Why is that? OOP encourages programmers to think about functions as things that are tucked inside data structures as “methods”. Classes themselves often rely on state and mutation.
Whereas with functional programming generally, and Racket in particular, we’re encouraged to think about data structures as things that are consumed by functions. And of course, to avoid state and mutation where possible. So it’s an inversion of priority.
Thus, as a consequence of the functional style, most Racketeers don’t find classes necesssary. But they aren’t avoiding OOP as a political gesture. If you want to rock it OOP-style in Racket, go ahead. No one will ever say you’re wrong.