Just like tuples, records can hold values of different types, but they are much more flexible than tuples.
Creating a Record
Records are created with curly brackets and their elements are separated by commas.
Unlike all data structures we have seen so far, records allow us to give names to the value contained in each element.
Let’s assign the record above to a constant and create a few more of these.
We created three records each containing information about a popular TV show. Let’s put them all in a list.
Here’s the output after some formatting to make it look nicer:
Creating records with the literal syntax (i.e. curly brackets) can be tedious. Is there a better way? Let’s find out. First, let’s give the structure that underlies each of the three records we just created a name.
type alias gives a name to an existing type. Using it we have given the underlying structure a name called
TVShow. Now we can create records much more cleanly.
With the new syntax, records are created by typing
TVShow followed by a name, creator, and number of episodes. This syntax looks familiar, doesn’t it? It’s basically a function application.
type alias created a function for constructing records in addition to giving the underlying structure a name.
It’s important to notice that when using
type alias to define a record structure, we must use
= to separate the property names and their types.
Records are values like everything else in Elm. We can pass them to a function as an argument, modify them, and return them back. Let’s create a function that checks to see if a TV show record has a creator or not.
hasCreator function accesses the value in
creator property using the dot syntax. Here are some more examples:
Special Accessor Functions
The second way to access elements in a record is with a special function.
.episodes are all special functions created behind the scenes when a record is created. These functions aren’t created by
type alias though. They are available to us even if we use the literal syntax instead of a constructor function to create a record.
Why do we call them special functions? First, they start with a dot. Normal functions aren’t allowed to start with special characters. Second, they can only be used on a record that actually has a label that matches the name of a special function. Let’s try using them to access a value in a record that doesn’t have the same labels.
.name though? Both records have a label called
That works because
.name function isn’t tied to any of the records we created before
sapiens. All it cares about is whether a record has a label called
name or not. These special functions are essentially equivalent to:
They take a record and return the value for the label represented by their name.
What do we gain by having special functions for accessing values in a record? After all the regular dot syntax works just fine and even looks more natural. To understand their usefulness, let’s try to sort TV shows by the number of episodes they have.
Here’s how the output looks after some formatting:
sortWith function from the
List module? We used it to sort a list of numbers in descending order like this:
sortWith accepts two arguments: a comparison function and a list that needs sorting.
sortByEpisodes is our comparison function that takes two TV shows and simply compares the number of episodes in them. As it turns out the
List module provides another function called
sortBy that makes sorting records much easier.
Here’s how the output looks after some formatting:
Instead of having to create a separate comparison function, we just tell
sortBy which property to use while sorting records.
sortBy then compares the specified properties and sorts a list accordingly. The first argument to
sortBy must still be a function though. Since
.episodes is a function, it’s perfectly fine to pass it to
sortBy. Here’s another example of
sortBy that sorts a list of strings by their length.
In the List section, we saw how to use the
map function to create a new list with the results of applying a given function on every element in a list. Since a list can also contain records in it, we can use
map to transform those records into anything we like.
We took a list of records and transformed it to a list of strings by applying an anonymous function that returns the value in
name property to every record. We can actually make the
map expression much nicer to read if we use the special function for accessing properties instead.
As you can see the special functions are quite useful when we want to pick just one property from each record and put them in a separate list.
Modifying a Record
Unlike tuples, we can modify values in a record. Remember all values in Elm are immutable and so are records. Because of that, Elm doesn’t really modify an existing record. It always returns a new one that contains the modified value. To understand how to modify a record, let’s create a function that increments the number of episodes by one.
As we can see, the number of episodes has been incremented to
15. But if we print the episodes in
firefly, it’s still
Elm didn’t change the record
firefly points to. It returned a new one. The syntax for modifying a record looks a bit weird, doesn’t it? Let’s deconstruct it.
We can also modify mutiple properties at the same time.
The expressions for updating each property must be separated by commas. If the expression becomes too long to fit in one line, it’s perfectly fine to break it into multiple lines.
Unlike all other data structures we’ve explored thus far, Elm doesn’t provide a separate module for records. That’s because other than the special accessors there are no functions available to manipulate records. Although that sounds limiting, a lot can be achieved just by using the special syntax for modifying records. Furthermore, the special accessor functions are generated on the fly after a record has been created. Therefore, we don’t need a separate module to house them.