LFE Style Guide
9 Data Representation
9.1 Empty Lists
Thanks to Erlang, LFE doesn't have any of the problems of ambiguity around
NIL
, empty lists, and 'false
:
- There is no
NIL
in LFE. If you want to indicate that something has no value, use the Erlang convention: return the'undefined
atom. - The empty
'()
list in LFE is just that: an empty list. It cannot be compared meaningfully to an undefined value or a boolean. - The boolean value for "False" in LFE is the atom
'false
. It cannot be meaningfully compared to an emoty list or an undefined value.
9.2 Do Not Abuse Lists
Even though back in 1958, LISP was short for "LISt Processing", its successors such as Common Lisp, Clojure, and LFE, have been modern programming languages with modern data structures since the 1980s and mid-2000s, respectively. You should use the appropriate data structures in your programs.
You should not abuse the builtin (single-linked) LIST data structure where it is not appropriate, even though LFE may make that easy to do.
You should only use lists when their performance characteristics is appropriate for the algorithm at hand: sequential iteration over the entire contents of the list.
An exception where it is appropriate to use lists is when it is known in advance that the size of the list will remain very short (say, less than 16 elements).
List data structures are often (but not always) appropriate for macros and functions used by macros at compile-time: indeed, not only is source code passed as lists in LFE, but the macro-expansion and compilation processes will typically walk over the entire source code sequentially.
Another exception where it is appropriate to use lists is for introducing literal constants that will be transformed into more appropriate data structures at compile-time or load-time. It is a good to have a function with a relatively short name to build your program's data structures from such literals.
In the many cases when lists are not the appropriate data structure, different data types and various libraries provide plenty of alternatives that should fulfill all the basic needs of your programs.
9.3 Lists vs. Tuples
For very simple tasks that require a little more structure than what lists
provide, you should use tuple
s.
If other parts of your application need to work with this data, if you need to be able to pass data around to functions, it's probably a good time to switch to using records.
9.4 Lists vs. Records
You should avoid using a list as anything besides a container of elements of like type. You should not use a list as method of passing multiple separate values of different types in and out of function calls. Sometimes it is convenient to use a list as a little ad hoc structure, i.e. "the first element of the list is a FOO, and the second is a BAR", but this should be used minimally since it gets harder to remember the little convention. You should only use a list that way when destructuring the list of arguments from a function, or creating a list of arguments to which to APPLY a function.
The proper way to pass around an object comprising several values of
heterogeneous types is to use a record as defined by (defrecord ...)
.
You should use multiple values only when a function returns a small number of values that are meant to be destructured immediately by the caller, rather than passed together as arguments to further functions.
9.5 Lists vs. Arrays
If you have code that requires data structures with nothing but numeric keys, arrays may be a good fit. Arrays provide access to elements with numerical indices. You can also fold over arrays while ignoring undefined slots.
Note that, unlike most other data structures you will find in LFE/Erlang which are 1-indexed, arrays are 0-indexed.
9.5 Lists vs. Sets
If you have data stored in lists and want to start doing union or intersection operations, it's time to switch to a set data type. Don't even think about writing macros for your lists. That'd just be silly.
LFE/Erlang has several to choose from:
- ordsets
- sets
- gb_sets
- sofs
9.6 Lists vs. Erlang Term Storage vs. Mnesia
If your data structures stored in lists need to be fast, support destructive updates, don't need garbage collection, and allow for other processes to access them, ETS tables might be just your thing.
If you need better concurrency than ETS provides, want transactions, or want to store your data in either memory or on disk, you might want to move your data into Mnesia.