Overview

Elm Friday: Type System Basics – Type Aliases and Records (Part IX)

No Comments

We have already touched the topic of Elm’s type system briefly (for example in post VI about type annotations) but Elm provides a few type constructs that we have not examined yet. We also talked about the advantages of having a strong type system, namely the stronger guarantees it enables as compared to dynamic languages like JavaScript. This boils down to “If it compiles, it’ll never throw a runtime exception”. In this episodes, we’ll revisit tuples and introduce type aliases and records.

About This Series

This is the ninth post in a series of short and sweet blog posts about Elm. The stated goal of this series is to take you from “completely clueless about Elm” to “chief Elm guru”, step by step. If you have missed the previous episodes, you might want to check out the table of contents.

Tuples

We have already used tuples in previous examples but since they are one of the basic building blocks for types, let’s review the concept shortly.

Tuples are similar to lists as they represent collections of multiple items. However, in a list, all elements need to have the same type. The number of elements is variable. In contrast, the elements of a tuple can all have different types but all tuples of the same type have the same length.

Here are some examples for a tuples (representing human beings with a name, weight in kilograms and their height in meters):

alice : (String, Int, Float)
alice = ("Alice", 61, 1.68)

bob : (String, Int, Float)
bob = ("Bob", 78, 1.82)

So, tuples are pairs (or triplets, quadruplets, …) of values. They are enclosed in ( and ) in Elm.

You can use pattern matching to access individual parts of the tuple. The following function takes a 3-tuple like in the example above and returns the name by doing a pattern matching on the incoming tuple. Since we do not care about the weight and the height here, we use the underscore (_) for those values.

getName : (String, Int, Float) -> String
getName (name, _, _) = name

Type Aliases

You can assign type aliases to make your code more readable. If the first element of a tuple is meant to represent a name, why not call it just that?

type alias Name = String
type alias Weight = Int
type alias Height = Float

alice : (Name, Weight, Height)
alice = ("Alice", 61, 1.68)

bob : (Name, Weight, Height)
bob = ("Bob", 78, 1.82)

Here, we used type aliases for basic types that are provided by Elm out of the box. You can use type aliases for any type construct you like, as we’ll see later.

Records

Tuples are one way to represent data structures. They are best suited for structures with only a few attributes, like pairs of values. For more structured data, Elm offers an alternative called records. This is how records looks like:

type alias Person =
  { name : String
  , weight : Int
  , height : Float
  }

alice : Person
alice =
  { name = "Alice"
  , weight = 61
  , height = 1.68
  }

bob : Person
bob =
  { name = "Bob"
  , weight = 78
  , height = 1.82 }

Note how we used a type alias here to have an identifier for the record type. This identifier (Person) can be used in type annotations then. The two values alice and bob show how to create new records.

There are a few more things that you can do with records and we will get to that in the next sections.

Access Record Attributes

Record attributes are accessed using a dot notation. To access the name of a Person record, you would write person.name, or person.height to access the person’s height attribute.

The dot notation can even be used as a standalone function. This is valid Elm code which converts a list of Person records into a list of their names:

toNames : List Person -> List String
toNames list =
  List.map .name list

Update Record Fields

Elm also provides a mechanism to update records. Since all values in Elm are immutable, “updating” a record translates to creating a copy of the original record and changing one (or several) attributes in the process.

The following example demonstrates how to update one attribute.

rename : Person -> String -> Person
rename person newName =
  { person | name = newName }

The next example updates multiple attributes at once.

grow : Person -> Int -> Float -> Person
grow person weightIncrease heightIncrease =
  { person |
      weight = person.weight + weightIncrease,
      height = person.height + heightIncrease }

That’s it for today. Now that we have reviewed tuples, type aliases and records, there is only one major type construct missing, which is called union types. This is covered in the next episode. Check it out!

Comment

Your email address will not be published. Required fields are marked *