3 Lists and Simple Data

3.5 Records

3.5.1 Just Records

Sometimes lists, tuples, property lists, or hashes are not quite what is needed. With tuples, you can't name keys (without awkward work-arounds), and this makes working with large tuples rather cumbersome. Records are one way around this.

A record is a data structure for storing a fixed number of elements. It has named fields and LFE provides some convenience functions/macros for interacting with them.

However, it is important to note that record expressions are translated to tuple expressions during compilation. Due to this, record expressions are not understood by the shell in both Erlang and LFE. The examples in this section, therefore, will assume that you are saving the code to a file.

Let's start by defining a record. Save this in a file named record.lfe:

(defmodule rec)

(defrecord person
  name
  address
  age)

Then load it up in the REPL:

> (slurp "record.lfe")
#(ok rec)
>

Now let's create some people:

> (set ford
    (make-person name "Ford Prefect"
                 address "Betelgeuse Seven"
                 age 234))
#(person "Ford Prefect" "Betelgeuse Seven" 234)
> (set trillian
    (make-person name "Tricia Marie McMillan"
                 age 60))
#(person "Tricia Marie McMillan" undefined 60)
>

Let's define a non-person, too:

> (set zaphod #("Zaphod Beeblebrox"))
#("Zaphod Beeblebrox")
>

Some quick checks:

> (is-person ford)
true
> (is-person zaphod)
false
>

If you remember working with the tuples, property lists, and dictionaries, then you will enjoy the relative succinctness of the following usages:

> (person-name ford)
"Ford Prefect"
> (person-address ford)
"Betelgeuse Seven"
> (person-age ford)
234
>

Let's make some changes to our data:

> (set ford
    (set-person-age ford 244))
#(person "Ford Prefect" "Betelgeuse Seven" 244)
> (person-age ford)
244
>

Just as we saw with the dict examples, set-person-age doesn't modify the data in-place, but rather returns a new record. If we want to use that data in the future, we'll need to assign it to a variable (sensibly, we re-use the ford variable here).

Also, note that there are also set-person-name and set-person-address.

3.5.2 Records and ETS

Additional convenience functions for records are provided by LFE, but some of these will only make sense in the context of ETS (Erlang Term Storage), when when the ability to store large amounts of data in memory becomes important. We will be discussing this in detail later, but this section provides a quick preview.

Let's create an ETS table:

> (set people
    (ets:new 'people-table '(#(keypos 2) set)))
16401
>

Now let's insert the two person records that we created above:

> (ets:insert people ford)
true
> (ets:insert people trillian)
true
>

Now that we have a table with some data in it, we can do some querying. Let's start with the emp-match LFE macro. Here's how we can get the name for every record in the table:

> (ets:match people (emp-person name '$1))
(("Ford Prefect") ("Tricia Marie McMillan"))
>

Or, we can adjust that to return the name and address:

> (ets:match people (emp-person name '$1 address '$2))
(("Ford Prefect" "Betelgeuse Seven") ("Tricia Marie McMillan" undefined))
>

With the match-person LFE macro, we can do more sophisticated querying

> (ets:select people
    (match-spec (((match-person name found-name age found-age))
                 (when (> 100 found-age))
                 found-name)))
> ("Tricia Marie McMillan")
>

Here we've done a select in the "people" table for any person whose age is less than 100.

Here's what it looks like when multiple records are returned:

> (ets:select people
    (match-spec (((match-person name found-name age found-age))
                 (when (< 21 found-age))
                 found-name)))
> ("Ford Prefect" "Tricia Marie McMillan")
>

This should be enough of an ETS taste to last until you get to the dedicated tutorial ;-)