Built-in Types
Elixir’s built-in types are
-
Value types:
- Arbitrary-sized integers
- Floating-point numbers
- Atoms
- Ranges
- Regular expressions
-
System types:
- PIDs and ports
- References
-
Collection types: – Tuples
- Lists
- Maps
- Binaries
- Integers
- Floating-Point Numbers
Atom
Atoms are constants that represent something’s name. We write them using a leading colon (:), An atom’s name is its value.
:fred :is_binary? :var@2 :<> :=== :"func/3"
:"long john silver" :эликсир :mötley_crüe
Ranges
Ranges are represented as start..end, where startand endare integers.
Regular Expression
You manipulate regular expressions with theRegex ## module.
iex> Regex.run ~r{[aeiou]}, "caterpillar" ["a"]
iex> Regex.scan ~r{[aeiou]}, "caterpillar" [["a"], ["e"], ["i"], ["a"]]
iex> Regex.split ~r{[aeiou]}, "caterpillar"
["c", "t", "rp", "ll", "r"]
iex> Regex.replace ~r{[aeiou]}, "caterpillar", "*" "c*t*rp*ll*r"
System Types
These types reflect resources in the underlying Erlang VM.
PIDs and Ports
A PID is a reference to a local or remote process. You can get PID of the current process by calling self
.
A port is a reference to a resource (normally being external) that you will be reading or writing.
Tuples
A tuple is an ordered collection of values. As with all Elixir data sturctures, once created a tuple cannot be modified.
{ 1, 2 } { :ok, 42, ”next”} { :error, :enoent}
A typical tuple has two to four elements, any more and you’ll probably want to look at maps
Tuples can be used in pattern matching:
iex> {status, count, action} = {:ok, 42, "next"} {:ok, 42, "next"}
iex> status
:ok
iex> count
42
iex> action
"next"
It is common for functions to return a tuple where the first element is the atom :ok
iex> {status, file} = File.open("mix.exs")
{:ok, #PID<0.39.0>}
Because the file was successfully opened, the tuple contains an :ok status and a PID, which is how we access the contents. A common practice is to write matches that assume success:
iex> { :ok, file } = File.open("mix.exs")
{:ok, #PID<0.39.0>}
iex> { :ok, file } = File.open("non-existent-file")
** (MatchError) no match of right hand side value: {:error, :enoent}
List
- A list is effectively a linked data structure in Elixir. List is not like an array in other languages. (in fact, tuples are the closest.)
- Lists are easy to traverse linearly, but expensive to access in random order. Some operators that work specifically on lists:
# concatenation
iex> [1,2,3] ++ [4,5,6]
[1, 2, 3, 4, 5, 6]
# difference
iex> [1, 2, 3, 4] -- [2, 4]
[1, 3]
# membership
iex> 1 in [1,2,3,4]
true
iex> "wombat" in [1, 2, 3, 4]
false
Keyword Lists
Lists of key/value pairs.
# If we write :
[ name: "Dave", city: "Dallas", likes: "Programming" ]
# Elixir converts it into a list of two-value tuples:
[ {:name, "Dave"}, {:city, "Dallas"}, {:likes, "Programming"} ]
Maps
A map is a collection of key/value pairs. A map literal looks like this:
%{ key => value, key => value }
Some example maps:
iex> states = %{ "AL" => "Alabama", "WI" => "Wisconsin" }
%{"AL" => "Alabama", "WI" => "Wisconsin"}
iex> responses = %{ { :error, :enoent } => :fatal, { :error, :busy } => :retry }
%{{:error, :busy} => :retry, {:error, :enoent} => :fatal}
iex> colors = %{ :red => 0xff0000, :green => 0x00ff00, :blue => 0x0000ff }
%{blue: 255, green: 65280, red: 16711680}
Immutability
The following code uses a new operator, [ head | tail ], which builds a new list with head as its first element and tail as the rest.
iex> list1 = [ 3, 2, 1 ] [3, 2, 1]
iex> list2 = [ 4 | list1 ] [4, 3, 2, 1]
In most languages, list2 would be built by creating a new list containing the values 4, 3, 2, and 1. The three values in list1 would be copied into the tail of list2. And that would be necessary because list1 would be mutable. But Elixir knows list1 will never change, so it simply constructs a new list with a head of 4 and a tail of list1.
Garbage Collection
The cool thing about Elixir is that you write your code using lots and lots of processes, and each process has its own heap. The data in your application is divvied up between these processes, so each individual heap is much, much smaller than would have been the case if all the data had been in a single heap. As a result, garbage collection runs faster. If a process terminates before its heap becomes full, all its data is discarded—no garbage collection is required.
Coding with Immutable Data
Any function that transforms data will return a new copy of it. Thus, we never capitalise a string. Instead, we return a capitalised copy of a string.
iex> name = "elixir"
"elixir"
iex> cap_name = String.capitalize name "Elixir"
iex> name
"elixir"
Pattern Matching
iex> a = 1
1
iex> a + 3
4
Most programmers would look at this code and say, “OK, we assign 1 to a variable a, then on the next line we add 3 to a, giving us 4.” But when it comes to Elixir, they’d be wrong. ~In Elixir, the equals sign is not an assignment.~ Instead it’s like an assertion. It succeeds if Elixir can find a way of making the left-hand side equal the right-hand side. Elixir calls the = symbol the match operator.
Pattern Matching Examples:
#example1
iex > list = [1, 2, 3]
[1, 2, 3]
iex > [a, b, c ] = list
[1, 2, 3]
iex > a 1
iex > b 2
iex > c 3
#example2
iex(1)> list =[1,2, [3,4,5] ]
[1, 2, [3, 4, 5]]
iex(2)> [a,b,c] = list
[1, 2, [3, 4, 5]]
iex(3)> a
1
iex(4)> b
2
iex(5)> c
[3, 4, 5]
Elixir looks for a way to make the value of the left side the same as the value of the right side. The left side is a list containing three variables, and the right is a list of three values, so the two sides could be made the same by setting the variables to the corresponding values. Elixir calls this process pattern matching.
iex> list = [1, 2, 3]
[1, 2, 3]
iex> [a, 1, b ] = list
** (MatchError) no match of right hand side value: [1, 2, 3]
Ignore a value with _
iex> [1, _, _] = [1, 2, 3]
[1, 2, 3]
iex> [1, _, _] = [1, "cat", "dog"]
[1, "cat", "dog"]
the _ acts like a variable but immediately discards any value given to it—in a pattern match, it is like a wildcard saying, “I’ll accept any value here.”
Variables bind once per match
iex> [a, a] = [1, 1]
[1, 1]
iex> a
1
iex> [b, b] = [1, 2]
** (MatchError) no match of right hand side value: [1, 2]
However, a variable can be bound to a new value in a subsequent match, and its current value does not participate in the new match.
iex> a = 1
1
iex> [1, a, 3] = [1, 2, 3]
[1, 2, 3]
iex> a
2
we can also use ^ the pin operator to force Elixir to use the existing value of the variable in the pattern.
#eg1
iex> a = 1
1
iex> a = 2
2
iex> ^a = 1
** (MatchError) no match of right hand side value: 1
####################################################################
#eg2
#This also works if the variable is a component of a pattern:
iex> a = 1
1
iex> [^a, 2, 3 ] = [ 1, 2, 3 ]
[1, 2, 3]
iex> a = 2
2
iex> [ ^a, 2 ] = [ 1, 2 ]
** (MatchError) no match of right hand side value: [1, 2]
- Elixir allows a match to reassign to a variable that was assigned in a prior match
- When you write the equation x = a + 1, you are not assigning the value of a + 1to x. Instead you’re simply asserting that the expressions xand a + 1have the same value. If you know the value of x, you can work out the value of a, and vice versa.
- you had to unlearn the algebraic meaning of = when you first came across assignment in imperative programming languages.
- All data is immutable.