Playing With Fire

Exploring the web one Elixir at a time

Elixir Types

Elixir has many of the types that you would expect from a modern programming language. The following list details these:

  • integers
  • floating-point numbers
  • atoms
  • ranges
  • regular expressions
  • PIDS (process ids)
  • ports
  • tuples
  • lists
  • keyword lists
  • maps
  • binaries
  • structs

what may or may not surprise you is that strings are absent from this list. That is because strings are lists, they are either lists of chars (character codes), denoted by single quotes or lists of binaries as denoted by double quotes.

In Elixir the concept of a string as used in imperative languages is the double quoted form and is stored as a sequence of bytes in UTF-8 encoding.

Strings and string operations will be covered in a different post.

 

Integers

Integers are whole numbers and can be of arbitrary size, usually dictated by the total amount of memory that you have in your system.

This value type can be represented in several different ways: decimal (1234), hexadecimal (0xcafe), octal (0o765) or binary (0b101010)

 

Floating-point Numbers

These conform to IEEE 754 double precision standard, meaning that they have about 16 digits of accuracy and a maximum exponent of 10308.

Standard decimal-point numbers with an optional trailing exponent are valid:

1.0

0.11111

1e12

3.14e-5

are all valid.

 

Atoms

For those that have not come across atoms before, they are constants the value of which is the same as its name. In Ruby they are called symbols and look exactly the same:

:atom

they can be made up of any combination of characters, but if you want to use anything that is not a valid letter, digit, at-signs or underscore, then you need to enclose it in double quotes:

:”this contains spaces”
:”func/3”

 

Ranges

A range is represented by start..end, where start and end are values of any type. They are usually integers to allow iteration.

iex(1)> 1..10
1..10
iex(2)> 1..10 |> Enum.map(&(&1))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 

Regular Expressions

Elixir has its own particular syntax for regular expression patterns. The patterns themselves are provided by PCRE. The syntax itself is: ~r{}, where is the pattern and are one or more single character options which are used to change the matching behaviour of the regex pattern.

From Elixir Regex: The modifiers available when creating a Regex are:

  • unicode (u) - enables unicode specific patterns like \p and changes modifiers like \w, \W, \s and friends to also match on unicode. It expects valid unicode strings to be given on match
  • caseless (i) - add case insensitivity
  • dotall (s) - causes dot to match newlines and also set newline to anycrlf; the new line setting can be overridden by setting (*CR) or (*LF) or (*CRLF) or (*ANY) according to re documentation
  • multiline (m) - causes ^ and $ to mark the beginning and end of each line; use \A and \z to match the end or beginning of the string
  • extended (x) - whitespace characters are ignored except when escaped and allow # to delimit comments
  • firstline (f) - forces the unanchored pattern to match before or at the first newline, though the matched text may continue over the newline
  • ungreedy (r) - inverts the “greediness” of the regexp

Example usage:

Regex.scan ~r{[aeiou]}, "caterpillar"
[["a"], ["e"], ["i"], ["a"]]

the delimiters ‘{‘ and ‘}’ can be replaced with any matching non-alphanumeric characters, like ‘%’ or ‘/‘. If ‘/‘ is used, then any ‘/‘ will need to be escaped in the regular expression pattern.

 

PID’s

PID’s are process ids. They are references to local or remote Elixir processes. PID’s are created automatically when processes are spawned. To get access to the PID of the current process you call self PID’s look like *<0.46.1>*

 

Ports

A port is reference to a resource, typically external to the application like a file, something that can be read from and/or written to.

 

Tuples

Tuples are collections of values written between ‘{‘ and ‘}’ with elements separated by commas:

iex(1)> {1,2,3,4}
{1,2,3,4}
iex(2)> {:ok, "This is a tuple", 42}
{:ok, "This is a tuple", 42}

Tuples are immutable, meaning that once created they cannot be changed. The element values do not need to be the same for each element.

Tuples are frequently used in pattern-matching:

iex(5)> {:ok, count, action} = {:ok, 128, "delete"}
{:ok, 128, "delete"}
iex(6)> count
128
iex(7)> action
"delete"

If the patterns on either side of the ‘=’ operator don’t match then:

iex(8)> {:error, reason} = {:ok, 128, "delete"}    
** (MatchError) no match of right hand side value: {:ok, 128, "delete"}

Tuples of different lengths are actually classed as different types. A Tuple with 2 elements is called a 2-tuple, with 3 elements a 3-tuple and so on.

 

Lists

Lists are represented by collections of values contained between ‘[‘ and ‘]’ and the values are separated by commas.

iex(1)> [1,2,3]
[1,2,3]

An empty list is represented by *[]*

A list is not an array, but a linked data structure which consists of a head and a tail. The head of a list is a value and the tail is itself a list.

iex(1)> [head | tail] = [1,2,3,4]
[1,2,3,4]
iex(2)> head
1
iex(3)> tail
[2,3,4]

Lists are generally traversed in sequence and this is cheap to do. To access elements in a list randomly is an expensive operation.

Lists will be further discussed in a different post.

 

Keyword Lists

Because it is a frequent requirement to have lists of key/value pairs, Elixir provides us some syntatic sugar:

iex(1)> [ key1: "value1", key2: "value2" ]

In Elixir, this is represented as a list of 2-tuples *[{:key1, “value1”}, {:key2, “value2”}]*

 

Maps

A map is a collection of key/value pairs.

iex(1)> %{ "key1" => "value1", "key2" => "value2" }

The keys in a map do not necessarily need to be strings. They can be atoms, tuples, or any other matchable type. Also the keys in a map do not need to be of the same type.

Values can be extracted from a Map using square bracket syntax:

iex(1)> postcode_towns = %{ "DN" => "Doncaster", "MK" => "Milton Keynes", "SW" => "Southwest London" }
%{ "DN" => "Doncaster", "MK" => "Milton Keynes", "SW" => "Southwest London" }
iex(2)> postcode_towns["DN"]
"Doncaster"

If the keys are atoms then we can use some syntatic sugar and treat it like a keyword list. In this case we can access the elements using dot-notation:

iex(1)> colours = %{ red: "#ff0000;", green: "#00ff00;", blue: "#0000ff;" }
%{ red: "#ff0000;", green: "#00ff00;", blue: "#0000ff;" }
iex(2)> colours.blue
"#0000ff;"
iex(3)> colours.pink
** (KeyError) key :pink not found in: %{blue: "#0000ff;", green: "#00ff00;", red: "#ff0000;"}

Question: why have both Keyword lists and Maps? Answer: because maps only allow unique keys, are highly optimised and can be used in pattern-matching, whereas in keyword lists the keys need not be unique and are generally used to pass around commandline arguments or options.

 

Binaries

Binaries are available in case you need to access data as a sequence of bits and bytes. Binary literals and delineated by *<<* and >>

 

Structs

Structs are a complex data type that requires a full post all to themselves.

With the whirlwind tour of types over, our next stop will be functions…