diff --git a/css/syntax.css b/css/syntax.css
new file mode 100644
index 000000000..2774b7649
--- /dev/null
+++ b/css/syntax.css
@@ -0,0 +1,60 @@
+.highlight { background: #ffffff; }
+.highlight .c { color: #999988; font-style: italic } /* Comment */
+.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
+.highlight .k { font-weight: bold } /* Keyword */
+.highlight .o { font-weight: bold } /* Operator */
+.highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */
+.highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
+.highlight .gd .x { color: #000000; background-color: #ffaaaa } /* Generic.Deleted.Specific */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #aa0000 } /* Generic.Error */
+.highlight .gh { color: #999999 } /* Generic.Heading */
+.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
+.highlight .gi .x { color: #000000; background-color: #aaffaa } /* Generic.Inserted.Specific */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #555555 } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #aaaaaa } /* Generic.Subheading */
+.highlight .gt { color: #aa0000 } /* Generic.Traceback */
+.highlight .kc { font-weight: bold } /* Keyword.Constant */
+.highlight .kd { font-weight: bold } /* Keyword.Declaration */
+.highlight .kp { font-weight: bold } /* Keyword.Pseudo */
+.highlight .kr { font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */
+.highlight .m { color: #009999 } /* Literal.Number */
+.highlight .s { color: #d14 } /* Literal.String */
+.highlight .na { color: #008080 } /* Name.Attribute */
+.highlight .nb { color: #0086B3 } /* Name.Builtin */
+.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */
+.highlight .no { color: #008080 } /* Name.Constant */
+.highlight .ni { color: #800080 } /* Name.Entity */
+.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */
+.highlight .nn { color: #555555 } /* Name.Namespace */
+.highlight .nt { color: #000080 } /* Name.Tag */
+.highlight .nv { color: #008080 } /* Name.Variable */
+.highlight .ow { font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #009999 } /* Literal.Number.Float */
+.highlight .mh { color: #009999 } /* Literal.Number.Hex */
+.highlight .mi { color: #009999 } /* Literal.Number.Integer */
+.highlight .mo { color: #009999 } /* Literal.Number.Oct */
+.highlight .sb { color: #d14 } /* Literal.String.Backtick */
+.highlight .sc { color: #d14 } /* Literal.String.Char */
+.highlight .sd { color: #d14 } /* Literal.String.Doc */
+.highlight .s2 { color: #d14 } /* Literal.String.Double */
+.highlight .se { color: #d14 } /* Literal.String.Escape */
+.highlight .sh { color: #d14 } /* Literal.String.Heredoc */
+.highlight .si { color: #d14 } /* Literal.String.Interpol */
+.highlight .sx { color: #d14 } /* Literal.String.Other */
+.highlight .sr { color: #009926 } /* Literal.String.Regex */
+.highlight .s1 { color: #d14 } /* Literal.String.Single */
+.highlight .ss { color: #990073 } /* Literal.String.Symbol */
+.highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #008080 } /* Name.Variable.Class */
+.highlight .vg { color: #008080 } /* Name.Variable.Global */
+.highlight .vi { color: #008080 } /* Name.Variable.Instance */
+.highlight .il { color: #009999 } /* Literal.Number.Integer.Long */
diff --git a/getting_started/1.markdown b/getting_started/1.markdown
index 8175f1075..be03269d1 100644
--- a/getting_started/1.markdown
+++ b/getting_started/1.markdown
@@ -31,12 +31,14 @@ If you don't use any of the distributions above, don't worry! Continue reading f
You can download and compile Elixir in few steps:
- $ git clone https://github.com/elixir-lang/elixir.git
- $ cd elixir
- $ make test
+{% highlight console %}
+$ git clone https://github.com/elixir-lang/elixir.git
+$ cd elixir
+$ make test
- $ bin/elixir -v
- Elixir 0.4.0.dev
+$ bin/elixir -v
+Elixir 0.4.0.dev
+{% endhighlight %}
If tests pass, you are ready to go. Otherwise, feel free to open an issue [in the issues tracker on Github](https://github.com/elixir-lang/elixir).
@@ -49,21 +51,25 @@ If tests pass, you are ready to go. Otherwise, feel free to open an issue [in th
We can start Elixir interactive mode by running `bin/iex` in the same directory you compiled Elixir or by simply running `iex` if you installed it from a distribution. In interactive mode, we can type any Elixir expression. Let's warm up with some basic arithmetic expressions:
- iex> 1 + 1
- 2
- iex> 10 - 5
- 5
- iex> 10 / 2
- 5.0
+{% highlight iex %}
+iex> 1 + 1
+2
+iex> 10 - 5
+5
+iex> 10 / 2
+5.0
+{% endhighlight %}
Notice `10 / 2` returned a float `5.0` instead of an integer. This is expected, in Elixir the operator `/` always returns a float. In case you want to do integer division or get the division remainder, you can invoke the `div` and `rem` functions:
- iex> div(10, 2)
- 5
- iex> div 10, 2
- 5
- iex> rem 10, 3
- 1
+{% highlight iex %}
+iex> div(10, 2)
+5
+iex> div 10, 2
+5
+iex> rem 10, 3
+1
+{% endhighlight %}
In the example above, we called two functions called `div` and `rem`. Notice that parenthesis are not required in order to invoke a function. We are going to discuss more about it later. Let's move forward and see which other data types we have in Elixir:
@@ -71,66 +77,84 @@ In the example above, we called two functions called `div` and `rem`. Notice tha
Some basic types are:
- iex> 1 # integer
- iex> 1.0 # float
- iex> :atom # atom / symbol
- iex> {1,2,3} # tuple
- iex> [1,2,3] # list
+{% highlight iex %}
+iex> 1 # integer
+iex> 1.0 # float
+iex> :atom # atom / symbol
+iex> {1,2,3} # tuple
+iex> [1,2,3] # list
+{% endhighlight %}
Elixir also provides functions (note the dot between the variable and arguments when calling a function):
- # function
- iex> x = fn(a, b) -> a + b end
- #Fun
- iex> x.(1, 2)
- 3
+{% highlight iex %}
+# function
+iex> x = fn(a, b) -> a + b end
+#Fun
+iex> x.(1, 2)
+3
+{% endhighlight %}
And double-quoted strings:
- iex> "string"
- "string"
+{% highlight iex %}
+iex> "string"
+"string"
+{% endhighlight %}
Strings in Elixir are UTF-8 binaries delimited by double quotes. A single-quoted string in Elixir is called a char list and is simply a list of characters:
- iex> 'string'
- 'string'
+{% highlight iex %}
+iex> 'string'
+'string'
+{% endhighlight %}
There is also string interpolation:
- iex> name = "world"
- iex> "hello #{name}"
- "hello world"
+{% highlight iex %}
+iex> name = "world"
+iex> "hello #{name}"
+"hello world"
+{% endhighlight %}
We can use the `is_binary` and `is_list` helpers to detect if a given string is a binary (double-quoted) or a list (single-quoted):
- iex> is_binary "string"
- true
- iex> is_list "string"
- false
+{% highlight iex %}
+iex> is_binary "string"
+true
+iex> is_list "string"
+false
- iex> is_binary 'string'
- false
- iex> is_list 'string'
- true
+iex> is_binary 'string'
+false
+iex> is_list 'string'
+true
+{% endhighlight %}
Although they represent the same thing, double-quoted and single-quoted strings are different and best suited for different scenarios:
- iex> "string" == 'string'
- false
+{% highlight iex %}
+iex> "string" == 'string'
+false
+{% endhighlight %}
Most of the cases, developers should use double-quoted strings as their representation is more compact. We are going to discuss this subject with more detail in the next chapter.
Elixir also provides `true` and `false` as booleans:
- iex> true
- true
- iex> is_boolean false
- true
+{% highlight iex %}
+iex> true
+true
+iex> is_boolean false
+true
+{% endhighlight %}
Booleans are represented internally as atoms:
- iex> is_atom(true)
- true
+{% highlight iex %}
+iex> is_atom(true)
+true
+{% endhighlight %}
Elixir also provides Port, References and PIDs as data types (usually used in process communication) but they are out of the scope of a getting started tutorial. For now, let's take a look at the basic operators in Elixir before we move on to the next chapter.
@@ -140,65 +164,83 @@ As we saw earlier, Elixir provides `+`, `-`, `*`, `/` as arithmetic operators.
Elixir also provides `++` and `--` to manipulate lists:
- iex> [1,2,3] ++ [4,5,6]
- [1,2,3,4,5,6]
- iex> [1,2,3] -- [2]
- [1,3]
+{% highlight iex %}
+iex> [1,2,3] ++ [4,5,6]
+[1,2,3,4,5,6]
+iex> [1,2,3] -- [2]
+[1,3]
+{% endhighlight %}
Since single-quoted strings are lists, we can also use `++` and `--` as operators to manipulate them:
- iex> 'some' ++ ' example'
- 'some example'
- iex> 'some' -- 'oe'
- 'sm'
+{% highlight iex %}
+iex> 'some' ++ ' example'
+'some example'
+iex> 'some' -- 'oe'
+'sm'
+{% endhighlight %}
Notice that `++` and `--` cannot be used to manipulate double quoted strings, as they are binaries. That said, string concatenation is done via `<>`:
- iex> "foo" <> "bar"
- "foobar"
+{% highlight iex %}
+iex> "foo" <> "bar"
+"foobar"
+{% endhighlight %}
Elixir also provides three boolean operators: `or`, `and` and `not`. Those operators are strict in the sense those operators expects only booleans (true or false) as arguments:
- iex> true and true
- true
- iex> false or is_atom(:example)
- true
+{% highlight iex %}
+iex> true and true
+true
+iex> false or is_atom(:example)
+true
+{% endhighlight %}
Giving a non-boolean as argument will raise an exception:
- iex> 1 and true
- ** (::ArgumentError) argument error
+{% highlight iex %}
+iex> 1 and true
+** (::ArgumentError) argument error
+{% endhighlight %}
`or` and `and` are short-circuit operators. They just execute the right side in case the left side is not enough to determine the result:
- iex> false and error("This error will never be raised")
- false
+{% highlight iex %}
+iex> false and error("This error will never be raised")
+false
- iex> true or error("This error will never be raised")
- true
+iex> true or error("This error will never be raised")
+true
+{% endhighlight %}
> Note: If you are an Erlang developer, `and` and `or` in Elixir actually map to the `andalso` and `orelse` operators in Erlang.
Elixir also provides `==`, `!=`, `===`, `!===`, `<=`, `>=`, `<` and `>` as comparison operators:
- iex> 1 == 1
- true
- iex> 1 != 2
- true
- iex> 1 < 2
- true
+{% highlight iex %}
+iex> 1 == 1
+true
+iex> 1 != 2
+true
+iex> 1 < 2
+true
+{% endhighlight %}
The difference between `==` and `===` is that the latter is more strict when comparing integers and floats:
- iex> 1 == 1.0
- true
- iex> 1 === 1.0
- false
+{% highlight iex %}
+iex> 1 == 1.0
+true
+iex> 1 === 1.0
+false
+{% endhighlight %}
In Elixir, we can compare two different data types:
- iex> 1 < :atom
- true
+{% highlight iex %}
+iex> 1 < :atom
+true
+{% endhighlight %}
The reason we can compare different data types is for pragmatism. Sorting algorithms don't need to worry about different data types in order to sort. The overall sorting order is defined below:
@@ -206,4 +248,4 @@ The reason we can compare different data types is for pragmatism. Sorting algori
You actually don't need to memorize this ordering, it is important just to know an order exists.
-Well, that is it for the introduction. In the next chapter, we are going to discuss some basic functions, data types conversions and a bit of control-flow.
\ No newline at end of file
+Well, that is it for the introduction. In the next chapter, we are going to discuss some basic functions, data types conversions and a bit of control-flow.
diff --git a/getting_started/2.markdown b/getting_started/2.markdown
index be2c1e3cd..f4f36d831 100644
--- a/getting_started/2.markdown
+++ b/getting_started/2.markdown
@@ -13,70 +13,86 @@ In this section we are going a bit deeper into the basic data-types, learn some
Elixir provides both lists and tuples:
- iex> is_list [1,2,3]
- true
- iex> is_tuple {1,2,3}
- true
+{% highlight iex %}
+iex> is_list [1,2,3]
+true
+iex> is_tuple {1,2,3}
+true
+{% endhighlight %}
While both are used to store items, they differ on how those items are stored in memory. Lists are implemented as linked lists (where each item in the list points to the next item) while tuples are stored contiguously in memory.
This means that accessing a tuple element is very fast (constant time) and can be achieved using the `elem` function (notice that indexes in Elixir data-types start with `1`):
- iex> elem { :a, :b, :c }, 1
- :a
+{% highlight iex %}
+iex> elem { :a, :b, :c }, 1
+:a
+{% endhighlight %}
On the other hand, updating a tuple is expensive as it needs to duplicate the tuple contents in memory. Updating a tuple can be done with the `setelem` function:
- iex> setelem { :a, :b, :c }, 1, :d
- {:d,:b,:c}
+{% highlight iex %}
+iex> setelem { :a, :b, :c }, 1, :d
+{:d,:b,:c}
+{% endhighlight %}
> Note: If you are an Erlang developer, you will notice that we used the `elem` and `setelem` functions instead of Erlang's `element` and `setelement`. The reason for this choice is that Elixir attempts to normalize Erlang API's to always receive the `subject` of the function as the first argument.
Since updating a tuple is expensive, when we want to iterate, add or remove elements, we use lists. Since lists are linked, it means accessing the first element of the list is very cheap, however, accessing the n-th element will require the algorithm to pass through n-1 nodes before reaching the n-th. We can access the `head` of the list as follows:
- iex> [head | tail] = [1,2,3]
- [1,2,3]
- iex> head
- 1
- iex> tail
- [2,3]
- iex> [head | tail]
- [1,2,3]
- iex> length [head | tail]
- 3
+{% highlight iex %}
+iex> [head | tail] = [1,2,3]
+[1,2,3]
+iex> head
+1
+iex> tail
+[2,3]
+iex> [head | tail]
+[1,2,3]
+iex> length [head | tail]
+3
+{% endhighlight %}
In the example above, we have assigned the head of the list to the variable `head` and the tail of the list to the variable `tail`. The [`Enum` module](https://github.com/elixir-lang/elixir/blob/master/lib/enum.ex) provides several helpers to manipulate lists (and other enumerables in general) while the [List module](https://github.com/elixir-lang/elixir/blob/master/lib/list.ex) provides several helpers specific to lists:
- iex> Enum.map [1,2,3], fn(x) -> x * 2 end
- [2,4,6]
- iex> List.flatten [1,[2],3]
- [1,2,3]
+{% highlight iex %}
+iex> Enum.map [1,2,3], fn(x) -> x * 2 end
+[2,4,6]
+iex> List.flatten [1,[2],3]
+[1,2,3]
+{% endhighlight %}
## 2.2 Lists and binaries
In the previous chapter we have discussed double- and single-quoted strings. Double quoted strings are binaries while single-quoted strings are lists:
- iex> "string" == 'string'
- false
- iex> is_binary "string"
- true
- iex> is_list 'string'
- true
+{% highlight iex %}
+iex> "string" == 'string'
+false
+iex> is_binary "string"
+true
+iex> is_list 'string'
+true
+{% endhighlight %}
In fact, both double-quoted and single-quoted representations are just a shorter representation of binaries and lists. Considering that `?a` in Elixir returns the ASCII integer for the letter `a`, we could also write:
- iex> <>
- "abc"
- iex> [?a, ?b, ?c]
- 'abc'
+{% highlight iex %}
+iex> <>
+"abc"
+iex> [?a, ?b, ?c]
+'abc'
+{% endhighlight %}
In such cases, Elixir detects that all characters in the list and in the binary are printable and returns the quoted representation. However, adding a non-printable character forces them to be printed differently:
- iex> <>
- <<97,98,99,1>>
+{% highlight iex %}
+iex> <>
+<<97,98,99,1>>
- iex> [?a, ?b, ?c, 1]
- [97,98,99,1]
+iex> [?a, ?b, ?c, 1]
+[97,98,99,1]
+{% endhighlight %}
Since lists are implemented as linked lists, it means a string represented as list usually takes a lot of space in memory (in ASCII, it would be one byte for each character and another byte to point to the next character). For this reason, binary (double-quoted) strings are preferred unless you want to explicitly iterate over the string as a list.
@@ -88,8 +104,10 @@ Elixir's plans is to provide a small standard library responsible for handling m
Erlang ships with a group of libraries called OTP (Open Telecom Platform). Besides an standard library, OTP provides several facilities to build OTP applications with supervisors that are robust, distributed and fault-tolerant. Invoking those libraries from Elixir is quite straight-forward, for example, we can call the [function `flatten` from the module `lists`](http://www.erlang.org/doc/man/lists.html#flatten-1) as follows:
- iex> Erlang.lists.flatten [1,[2],3]
- [1,2,3]
+{% highlight iex %}
+iex> Erlang.lists.flatten [1,[2],3]
+[1,2,3]
+{% endhighlight %}
Erlang's OTP is very well documented and a developer should not have problems going around it:
@@ -100,162 +118,206 @@ Erlang's OTP is very well documented and a developer should not have problems go
When discussing lists, we saw the following example:
- iex> [h | t] = [1,2,3]
- [1, 2, 3]
- iex> h
- 1
- iex> t
- [2, 3]
+{% highlight iex %}
+iex> [h | t] = [1,2,3]
+[1, 2, 3]
+iex> h
+1
+iex> t
+[2, 3]
+{% endhighlight %}
In Elixir, `=` does not mean assignment as in programming languages like Java and Ruby. `=` is actually a match operator which will check if the expressions on both left and right side match. Consider this example:
- iex> { 1, 2, 3 } = { 1, 2, 3 }
- { 1, 2, 3 }
- iex> { 1, 2, 3 } = { 1, 4, 3 }
- ** (::MatchError) no match of right hand side value: {1,4,3}
+{% highlight iex %}
+iex> { 1, 2, 3 } = { 1, 2, 3 }
+{ 1, 2, 3 }
+iex> { 1, 2, 3 } = { 1, 4, 3 }
+** (::MatchError) no match of right hand side value: {1,4,3}
+{% endhighlight %}
If the tuples given on the left and right side do not match, an error is raised. If any of the tuples contain a variable, this variable will always be assigned:
- iex> { 1, x, 3 } = { 1, 2, 3 }
- { 1, 2, 3 }
- iex> x
- 2
- iex> { 1, x, 3 } = { 1, 4, 3 }
- { 1, 4, 3 }
- iex> x
- 4
+{% highlight iex %}
+iex> { 1, x, 3 } = { 1, 2, 3 }
+{ 1, 2, 3 }
+iex> x
+2
+iex> { 1, x, 3 } = { 1, 4, 3 }
+{ 1, 4, 3 }
+iex> x
+4
+{% endhighlight %}
This is exactly what happened in the list example:
- iex> [h | t] = [1,2,3]
- [1, 2, 3]
+{% highlight iex %}
+iex> [h | t] = [1,2,3]
+[1, 2, 3]
+{% endhighlight %}
We have assigned the head of the list to `h` and the tail to `t`. In fact, if we want to check if the head of the list is `1` and assign the tail, we could do:
- iex> [1 | t] = [1,2,3]
- [1, 2, 3]
- iex> [0 | t] = [1,2,3]
- ** (::MatchError) no match of right hand side value: [1,2,3]
+{% highlight iex %}
+iex> [1 | t] = [1,2,3]
+[1, 2, 3]
+iex> [0 | t] = [1,2,3]
+** (::MatchError) no match of right hand side value: [1,2,3]
+{% endhighlight %}
In case you want to pattern match against the value of a variable, you can use the `^` operator:
- iex> x = 1
- 1
- iex> ^x = 1
- 1
- iex> ^x = 2
- ** (::MatchError) no match of right hand side value: 2
- iex> x = 2
- 2
+{% highlight iex %}
+iex> x = 1
+1
+iex> ^x = 1
+1
+iex> ^x = 2
+** (::MatchError) no match of right hand side value: 2
+iex> x = 2
+2
+{% endhighlight %}
In Elixir, it is a common practice to assign a variable to underscore `_` if we don't intend to use it. For example, if only the head of the list matters to us, we can assign the tail to underscore:
- iex> [h | _] = [1,2,3]
- [1, 2, 3]
- iex> h
- 1
+{% highlight iex %}
+iex> [h | _] = [1,2,3]
+[1, 2, 3]
+iex> h
+1
+{% endhighlight %}
The variable `_` in Elixir is special in the sense it can never be assigned. Trying to read from it gives an unbound variable error:
- iex> _
- ** (ErlangError) erlang error {:unbound_var, :_}
+{% highlight iex %}
+iex> _
+** (ErlangError) erlang error {:unbound_var, :_}
+{% endhighlight %}
Although pattern matching allow powerful constructs, its usage is limited. For instance, you cannot make function calls on the left side of the match. The following example is invalid:
- iex> Erlang.lists.flatten([1,[2],3]) = [1,2,3]
- ** (ErlangError) erlang error :illegal_pattern
+{% highlight iex %}
+iex> Erlang.lists.flatten([1,[2],3]) = [1,2,3]
+** (ErlangError) erlang error :illegal_pattern
+{% endhighlight %}
## 2.5 Key-values
One of the first control flow constructs we usually learn is the conditional `if`. In Elixir, we can write `if` in these two equivalent ways:
- iex> if true, do: 1 + 2
- 3
- iex> if true do
- ...> 1 + 2
- ...> end
- 3
+{% highlight iex %}
+iex> if true, do: 1 + 2
+3
+iex> if true do
+...> 1 + 2
+...> end
+3
+{% endhighlight %}
Both examples above are simply different ways of expressing key-value arguments. Key-value arguments are a list of two-item tuples, where the first element is an atom representing the key and the second is the value. Elixir provides a syntax-shortcut for creating such key-values:
- iex> [a: 1, b: 2]
- [{:a, 1}, {:b, 2}]
+{% highlight iex %}
+iex> [a: 1, b: 2]
+[{:a, 1}, {:b, 2}]
+{% endhighlight %}
In order to manipulate those key-value arguments, we can use the [`Orddict` module](https://github.com/elixir-lang/elixir/blob/master/lib/orddict.ex):
- iex> x = [a: 1, b: 2]
- [{:a, 1}, {:b, 2}]
- iex> Orddict.get x, :a
- 1
- iex> Orddict.get x, :c
- nil
+{% highlight iex %}
+iex> x = [a: 1, b: 2]
+[{:a, 1}, {:b, 2}]
+iex> Orddict.get x, :a
+1
+iex> Orddict.get x, :c
+nil
+{% endhighlight %}
Going back to the `if` example, we invoked it passing a condition (`true`) and a key-value argument (`do: 1 + 2`):
- iex> if true, do: 1 + 2
- 3
+{% highlight iex %}
+iex> if true, do: 1 + 2
+3
+{% endhighlight %}
Since the key-value argument is the last argument, the brackets are optional. These are all equivalent:
- iex> if true, do: 1 + 2
- 3
- iex> if true, [do: 1 + 2]
- 3
- iex> if(true, [do: 1 + 2])
- 3
+{% highlight iex %}
+iex> if true, do: 1 + 2
+3
+iex> if true, [do: 1 + 2]
+3
+iex> if(true, [do: 1 + 2])
+3
+{% endhighlight %}
Besides, we can also pass an `else` clause to `if`:
- iex> if false, do: 1 + 2, else: 10 + 3
- 13
+{% highlight iex %}
+iex> if false, do: 1 + 2, else: 10 + 3
+13
+{% endhighlight %}
However, most of the times `if` clauses are longer than the examples above. In such cases, we usually use the block format:
- iex> if true do
- ...> 1 + 2
- ...> end
+{% highlight iex %}
+iex> if true do
+...> 1 + 2
+...> end
+{% endhighlight %}
Internally, this is converted to the same key-value arguments as above. This feature is called key-value blocks. You can pass `else:` as option as well:
- if false do
- 1 + 2
- else:
- 10 + 3
- end
+{% highlight elixir %}
+if false do
+ 1 + 2
+else:
+ 10 + 3
+end
+{% endhighlight %}
Key-value blocks are similar to Ruby blocks. Parenthesis can be added as follows:
- if(false) do
- 1 + 2
- else:
- 10 + 3
- end
+{% highlight elixir %}
+if(false) do
+ 1 + 2
+else:
+ 10 + 3
+end
+{% endhighlight %}
Key-value blocks are an important feature that allow developers to create their own control structures as if they were part of the language itself. For instance, none of the control structures we are going to see in the next section are keywords. They are all implemented using key-values blocks.
Elixir supports two syntaxes for key-value blocks: `do`/`end` and `->`/`end`. The first one always binds to the farthest function call, while the latter to the closest. For example, the following expression:
- Enum.map [1,2,3], fn(x) do
- x * 2
- end
+{% highlight elixir %}
+Enum.map [1,2,3], fn(x) do
+ x * 2
+end
+{% endhighlight %}
Would be parsed as:
- Enum.map([1,2,3], fn(x)) do
- x * 2
- end
+{% highlight elixir %}
+Enum.map([1,2,3], fn(x)) do
+ x * 2
+end
+{% endhighlight %}
Which is not what we want since `do` is binding to the farthest function call, in this case: `Enum.map`. We can fix this by using `->`, forcing the key-value block to bind to the `fn`:
- Enum.map [1,2,3], fn(x) ->
- x * 2
- end
+{% highlight elixir %}
+Enum.map [1,2,3], fn(x) ->
+ x * 2
+end
+{% endhighlight %}
Which is then parsed as:
- Enum.map([1,2,3], fn(x) ->
- x * 2
- end)
+{% highlight elixir %}
+Enum.map([1,2,3], fn(x) ->
+ x * 2
+end)
+{% endhighlight %}
A good rule of thumb is: always use `->/end` when defining functions with `fn`, use `do/end` for all other structures. If we follow this rule, everything works transparently.
@@ -267,32 +329,36 @@ In this section we are going to describe Elixir main control structures.
Refreshing from the section above, all these calls are equivalent:
- if false, do: 1 + 2, else: 10 + 3
+{% highlight elixir %}
+if false, do: 1 + 2, else: 10 + 3
- if false do
- 1 + 2
- else:
- 10 + 3
- end
+if false do
+ 1 + 2
+else:
+ 10 + 3
+end
- # Although this is valid, its usage is discouraged.
- if(false) ->
- 1 + 2
- else:
- 10 + 3
- end
+# Although this is valid, its usage is discouraged.
+if(false) ->
+ 1 + 2
+else:
+ 10 + 3
+end
+{% endhighlight %}
`if` also accepts many `elsif:` clauses:
- if 1 + 1 == 3 do
- IO.puts "Impossible"
- elsif: 1 + 1 == 2
- IO.puts "This will match"
- elsif: true
- IO.puts "This won't because the one above matched"
- else:
- IO.puts "This won't"
- end
+{% highlight elixir %}
+if 1 + 1 == 3 do
+ IO.puts "Impossible"
+elsif: 1 + 1 == 2
+ IO.puts "This will match"
+elsif: true
+ IO.puts "This won't because the one above matched"
+else:
+ IO.puts "This won't"
+end
+{% endhighlight %}
In Elixir, all values except `false` and `nil` evaluates to true. So there is no need to convert them to false.
@@ -302,59 +368,67 @@ In the previous chapter, we discussed the boolean operators `and`, `or` and `not
To work around this limitation, Elixir provides three operators with similar functionality but that accept any argument: `||`, `&&` and `!`. For those operators, all values except `false` and `nil` will evaluate to true.
- # Short-circuit or
- iex> 1 || true
- 1
- iex> false || 11
- 11
-
- # Short-circuit and
- iex> nil && 13
- nil
- iex> true && 17
- 17
-
- # Short-circuit !
- iex> !true
- false
- iex> !1
- false
- iex> !nil
- true
+{% highlight elixir %}
+# Short-circuit or
+iex> 1 || true
+1
+iex> false || 11
+11
+
+# Short-circuit and
+iex> nil && 13
+nil
+iex> true && 17
+17
+
+# Short-circuit !
+iex> !true
+false
+iex> !1
+false
+iex> !nil
+true
+{% endhighlight %}
### 2.6.3 Case
In this section we have introduced pattern matching via the `=` operator. Sometimes however it is convenient to match an expression against several expressions until we find a matching one. For such cases, we use `case`:
- case { 1, 2, 3 } do
- match: { 4, 5, 6 }
- IO.puts "This won't match"
- match: { 1, x, 3 }
- IO.puts "This will match and assign x"
- else:
- IO.puts "No match"
- end
+{% highlight elixir %}
+case { 1, 2, 3 } do
+match: { 4, 5, 6 }
+ IO.puts "This won't match"
+match: { 1, x, 3 }
+ IO.puts "This will match and assign x"
+else:
+ IO.puts "No match"
+end
+{% endhighlight %}
As in the `=` operator, any assigned variable will be overridden in the match clause. In case you want to pattern match against a variable, you need to use the `^` operator:
- x = 1
- case 10 do
- match: ^x
- IO.puts "Won't match"
- else:
- IO.puts "Will match"
- end
+{% highlight elixir %}
+x = 1
+case 10 do
+match: ^x
+ IO.puts "Won't match"
+else:
+ IO.puts "Will match"
+end
+{% endhighlight %}
Each match clause also supports special conditions to be given via guards:
- case { 1, 2, 3 } do
- match: { 4, 5, 6 }
- IO.puts "This won't match"
- match: { 1, x, 3 } when x > 0
- IO.puts "This will match and assign x"
- else:
- IO.puts "No match"
- end
+{% highlight elixir %}
+case { 1, 2, 3 } do
+match: { 4, 5, 6 }
+ IO.puts "This won't match"
+match: { 1, x, 3 } when x > 0
+ IO.puts "This will match and assign x"
+else:
+ IO.puts "No match"
+end
+{% endhighlight %}
In the example above, the second clause will only match when x is positive. The Erlang VM only allows few expressions as guards, they are:
@@ -402,18 +476,22 @@ In the example above, the second clause will only match when x is positive. The
Many independent guard clauses can also be given at the same time. For example, consider a function that checks if the first element of a tuple or a list is zero. It could be written as:
- def first_is_zero?(tuple_or_list) when
- elem(tuple_or_list, 1) == 0 or hd(tuple_or_list) == 0 do
- true
- end
+{% highlight elixir %}
+def first_is_zero?(tuple_or_list) when
+ elem(tuple_or_list, 1) == 0 or hd(tuple_or_list) == 0 do
+ true
+end
+{% endhighlight %}
However, the example above will always fail because, if the argument is a list, calling `elem` in a list will raise an error. On the other hand, if the element is a tuple, calling `hd` in a tuple will also raise an error. That said, we can rewrite it to become two different clauses:
- def first_is_zero?(tuple_or_list) when
- elem(tuple_or_list, 1) == 0 when
- hd(tuple_or_list) == 0 do
- true
- end
+{% highlight elixir %}
+def first_is_zero?(tuple_or_list) when
+ elem(tuple_or_list, 1) == 0 when
+ hd(tuple_or_list) == 0 do
+ true
+end
+{% endhighlight %}
In such cases, if there is an error in one of the guards, it won't affect the next one.
@@ -421,48 +499,60 @@ In such cases, if there is an error in one of the guards, it won't affect the ne
Throughout this guide, we have created many functions in examples. The syntax for creating functions is:
- fn(a, b) -> a + b end
+{% highlight elixir %}
+fn(a, b) -> a + b end
+{% endhighlight %}
But it could also be written as (the previous example is preferred though):
- fn(a, b, do: a + b)
+{% highlight elixir %}
+fn(a, b, do: a + b)
- fn(a, b) do
- a + b
- end
+fn(a, b) do
+ a + b
+end
+{% endhighlight %}
As an immutable language, the binding of the function is also immutable. This means that setting a variable inside the function does not affect its outer scope:
- x = 1
- (fn -> x = 2 end).()
- x #=> 1
+{% highlight elixir %}
+x = 1
+(fn -> x = 2 end).()
+x #=> 1
+{% endhighlight %}
### 2.6.5 Loops
Due to data structure immutability, loops in Elixir (and in functional programming languages) are written differently from conventional imperative languages. For example, in an imperative language, one would write:
- for(i = 0; i < array.length; i++) {
- array[i] = array[i] * 2
- }
+{% highlight elixir %}
+for(i = 0; i < array.length; i++) {
+ array[i] = array[i] * 2
+}
+{% endhighlight %}
In the example above, we are mutating the array which is not possible in Elixir. Therefore, in functional languages recursion happens by calling an anonymous or a named function recursively, until we reach a condition. Consider the example below that manually sums all the items in the list:
- iex> loop [1,2,3], 0 do
- ...> match: [h|t], acc
- ...> recur(t, h + acc)
- ...> match: [], acc
- ...> acc
- ...> end
- 6
+{% highlight iex %}
+iex> loop [1,2,3], 0 do
+...> match: [h|t], acc
+...> recur(t, h + acc)
+...> match: [], acc
+...> acc
+...> end
+6
+{% endhighlight %}
In the example above, we pass a list `[1,2,3]` and the initial value `0` as arguments to loop. The list `[1,2,3]` is then matched against `[h|t]` which assigns `h = 1` and `t = [2,3]` and 0 is assigned to `acc`.
Then, we add the head of the list to the accumulator `h + acc` and call the loop again using the `recur` function, passing the tail of the list as argument. The tail will once again match the `[h|t]` until the list is empty, matching the final clause which returns the final result of `6`. In other words, the loop is called 4 times until the list is empty and the recursion stops:
- loop [1,2,3], 0
- loop [2,3], 1
- loop [3], 3
- loop [], 6
+{% highlight elixir %}
+loop [1,2,3], 0
+loop [2,3], 1
+loop [3], 3
+loop [], 6
+{% endhighlight %}
> Note: `loop/recur` is also a Clojure idiom, although differently from Clojure, `recur` in Elixir does not ensure a tail call was made.
@@ -470,79 +560,89 @@ Then, we add the head of the list to the accumulator `h + acc` and call the loop
The next control-flow mechanism is `try/catch/after`:
- iex> try do
- ...> throw 13
- ...> catch: number
- ...> number
- ...> end
- 13
+{% highlight iex %}
+iex> try do
+...> throw 13
+...> catch: number
+...> number
+...> end
+13
+{% endhighlight %}
`try/catch` is the main mechanism for catching values thrown by Elixir runtime. It also supports an `after` clause that is invoked regardless if the value was caught or not:
- iex> try do
- ...> throw 13
- ...> catch: nan when not is_number(nan)
- ...> nan
- ...> after:
- ...> IO.puts "Didn't catch"
- ...> end
- Didn't catch
- ** throw 13
- erl_eval:expr/3
+{% highlight iex %}
+iex> try do
+...> throw 13
+...> catch: nan when not is_number(nan)
+...> nan
+...> after:
+...> IO.puts "Didn't catch"
+...> end
+Didn't catch
+** throw 13
+ erl_eval:expr/3
+{% endhighlight %}
There is one particularity that applies to `try/catch/after` when compared to other control-flow expressions. The Erlang VM considers such clauses unsafe (since they may fail or not) and do not allow variables defined inside `try/catch/after` to be accessed from the outer scope:
- iex> try do
- ...> new_var = 1
- ...> catch: value
- ...> value
- ...> end
- 1
- iex> new_var
- ** error :undef
+{% highlight iex %}
+iex> try do
+...> new_var = 1
+...> catch: value
+...> value
+...> end
+1
+iex> new_var
+** error :undef
+{% endhighlight %}
The common strategy then is to make explicit all arguments that are required after the `try`:
- { x, y } = try do
- x = calculate_some_value()
- y = some_other_value()
- { x, y }
- catch: _
- { nil, nil }
- end
+{% highlight elixir %}
+{ x, y } = try do
+ x = calculate_some_value()
+ y = some_other_value()
+ { x, y }
+catch: _
+ { nil, nil }
+end
- x #=> returns the value of x or nil for failures
+x #=> returns the value of x or nil for failures
+{% endhighlight %}
### 2.6.7 Rescue
While `catch` clauses inside `try` are simply a pattern matching mechanism, `rescue` provides a higher abstraction around exceptions. `rescue` allows a developer to rescue an exception by its name and not by its internal contents. Consider the following examples:
- try do
- raise "some error"
- rescue: RuntimeError
- "rescued"
- end
-
- try do
- raise "some error"
- rescue: [RuntimeError]
- "rescued"
- end
-
- # rescue and assign to x
- try do
- raise "some error"
- rescue: x in [RuntimeError]
- # all exceptions respond to message
- x.message
- end
-
- # rescue all (discouraged) and assign to x
- try do
- raise ArgumentError, message: "unexpected argument"
- rescue: x in _
- x.message
- end
+{% highlight elixir %}
+try do
+ raise "some error"
+rescue: RuntimeError
+ "rescued"
+end
+
+try do
+ raise "some error"
+rescue: [RuntimeError]
+ "rescued"
+end
+
+# rescue and assign to x
+try do
+ raise "some error"
+rescue: x in [RuntimeError]
+ # all exceptions respond to message
+ x.message
+end
+
+# rescue all (discouraged) and assign to x
+try do
+ raise ArgumentError, message: "unexpected argument"
+rescue: x in _
+ x.message
+end
+{% endhighlight %}
Custom exceptions can be defined using the `defexception` macro. Check [the exceptions file for some examples](https://github.com/elixir-lang/elixir/tree/master/lib/exception.ex).
@@ -552,29 +652,33 @@ The last control-flow mechanism we are going to discuss is essential to Elixir's
In order to exchange messages, each process has a mailbox where the received messages are stored. The `receive` mechanism allows us to go through this mailbox searching for a message that matches the given pattern. Here is an example that uses the arrow operator `<-` to send a message to the current process and then collects this message from its mailbox:
- # Get the current process id
- iex> current_pid = self()
+{% highlight iex %}
+# Get the current process id
+iex> current_pid = self()
- # Spawn another process that will send a message to current_pid
- iex> spawn fn(do: current_pid <- { :hello, self() })
- <0.36.0>
+# Spawn another process that will send a message to current_pid
+iex> spawn fn(do: current_pid <- { :hello, self() })
+<0.36.0>
- # Collect the message
- iex> receive do
- ...> match: { :hello, pid }
- ...> IO.puts "Hello from #{inspect(pid)}"
- ...> end
- Hello from <0.36.0>
+# Collect the message
+iex> receive do
+...> match: { :hello, pid }
+...> IO.puts "Hello from #{inspect(pid)}"
+...> end
+Hello from <0.36.0>
+{% endhighlight %}
You may not see exactly `<0.36.0>` back, but something similar. If there are no messages in the mailbox, the current process will hang until a matching message arrives, unless an after clause is given:
- iex> receive do
- ...> match: :waiting
- ...> IO.puts "This may never come"
- ...> after: 1000 # 1 second
- ...> IO.puts "Too late"
- ...> end
- Too late
+{% highlight iex %}
+iex> receive do
+...> match: :waiting
+...> IO.puts "This may never come"
+...> after: 1000 # 1 second
+...> IO.puts "Too late"
+...> end
+Too late
+{% endhighlight %}
In most cases, we don't send messages directly with `<-` nor write `receive` control expressions. Instead, we use many of the abstractions provided by OTP which will be discussed later.
@@ -584,4 +688,4 @@ Elixir ships with many default functions automatically available in the current
Besides the functions provided by Elixir, Elixir imports many of the root function from Erlang. The function `length`, `is_list`, `is_number` and many others we discussed above comes from Erlang. [The full documented list is available on the OTP documentation page](http://www.erlang.org/doc/man/erlang.html).
-All those functions and control flow expressions are essential for building Elixir programs. The next chapter will discuss how to organize our code into modules, so it can be easily re-used between different components.
\ No newline at end of file
+All those functions and control flow expressions are essential for building Elixir programs. The next chapter will discuss how to organize our code into modules, so it can be easily re-used between different components.
diff --git a/getting_started/3.markdown b/getting_started/3.markdown
index 13f38fe03..f655430f8 100644
--- a/getting_started/3.markdown
+++ b/getting_started/3.markdown
@@ -9,19 +9,23 @@ guide: 3
In Elixir, you can group several functions into a module. In the previous chapter, we have invoked for example functions from the module List:
- iex> List.flatten [1,[2],3]
- [1, 2, 3]
+{% highlight iex %}
+iex> List.flatten [1,[2],3]
+[1, 2, 3]
+{% endhighlight %}
In order to create our own modules in Elixir, all we have to do is to call the `defmodule` function and use `def` to define our functions:
- iex> defmodule Math do
- ...> def sum(a, b) do
- ...> a + b
- ...> end
- ...> end
+{% highlight iex %}
+iex> defmodule Math do
+...> def sum(a, b) do
+...> a + b
+...> end
+...> end
- iex> Math.sum(1, 2)
- 3
+iex> Math.sum(1, 2)
+3
+{% endhighlight %}
Before diving into modules, let's first have a brief overview about compilation.
@@ -29,11 +33,13 @@ Before diving into modules, let's first have a brief overview about compilation.
Most of the times it is convenient to write modules into files so they can be compiled and re-used. Let's assume we have a file named `math.ex` with the following contents:
- defmodule Math do
- def sum(a, b) do
- a + b
- end
- end
+{% highlight elixir %}
+defmodule Math do
+ def sum(a, b) do
+ a + b
+ end
+end
+{% endhighlight %}
This file can be compiled using `bin/elixirc` (or simply `elixirc` if you installed Elixir via a distribution):
@@ -41,8 +47,10 @@ This file can be compiled using `bin/elixirc` (or simply `elixirc` if you instal
Which will then generate a file named `::Math.beam` containing the bytecode for the defined module. Now, if we start `bin/iex` again, our module definition will be available (considering `bin/iex` is being started in the same directory the bytecode file is):
- iex> Math.sum(1, 2)
- 3
+{% highlight iex %}
+iex> Math.sum(1, 2)
+3
+{% endhighlight %}
Elixir projects are usually organized into three directories:
@@ -60,13 +68,15 @@ Where `-pa` stands for `path append`. The same option can also be passed to `eli
Besides the Elixir file `.ex`, Elixir also supports `.exs` files for scripting. Elixir treats both files exactly the same way, the only difference is in intention. `.ex` files are meant to be compiled while `.exs` files are used for scripting, without a need for compilation. For instance, one can create a file called `math.exs`:
- defmodule Math do
- def sum(a, b) do
- a + b
- end
- end
+{% highlight elixir %}
+defmodule Math do
+ def sum(a, b) do
+ a + b
+ end
+end
- IO.puts Math.sum(1, 2)
+IO.puts Math.sum(1, 2)
+{% endhighlight %}
And execute it as:
@@ -78,36 +88,40 @@ The file will be compiled in memory and executed, printing 3 as result. No bytec
Inside a module, we can define functions with `def` and private functions with `defp`. A function defined with `def` is available to be invoked from other modules while a private function can only be invoked locally.
- defmodule Math do
- def sum(a, b) do
- do_sum(a, b)
- end
+{% highlight elixir %}
+defmodule Math do
+ def sum(a, b) do
+ do_sum(a, b)
+ end
- defp do_sum(a, b) do
- a + b
- end
- end
+ defp do_sum(a, b) do
+ a + b
+ end
+end
- Math.sum(1, 2) #=> 3
- Math.do_sum(1, 2) #=> ** (UndefinedFunctionError)
+Math.sum(1, 2) #=> 3
+Math.do_sum(1, 2) #=> ** (UndefinedFunctionError)
+{% endhighlight %}
Function declarations also supports guards and multiple clauses. If a function has several clauses, Elixir will try each clause until find one that matches. For example, here is the implementation for a function that checks if the given number is zero or not:
- defmodule Math do
- def zero?(0) do
- true
- end
+{% highlight elixir %}
+defmodule Math do
+ def zero?(0) do
+ true
+ end
- def zero?(x) when is_number(x) do
- false
- end
- end
+ def zero?(x) when is_number(x) do
+ false
+ end
+end
- Math.zero?(0) #=> true
- Math.zero?(1) #=> false
+Math.zero?(0) #=> true
+Math.zero?(1) #=> false
- Math.zero?([1,2,3])
- #=> ** (FunctionClauseError)
+Math.zero?([1,2,3])
+#=> ** (FunctionClauseError)
+{% endhighlight %}
Notice that giving an argument that does not match any of the clauses raises an error.
@@ -119,28 +133,34 @@ In order to support software-reuse, Elixir supports two directives. As we are go
We use `import` whenever we want to easily access functions or macros from others modules without using the qualified name. For instance, if we want to use the `values` function from `Orddict` several times in a module and we don't want to always type `Orddict.values`, we can simply import it:
- defmodule Math do
- import Orddict, only: [values: 1]
+{% highlight elixir %}
+defmodule Math do
+ import Orddict, only: [values: 1]
- def some_function do
- # call values(orddict)
- end
- end
+ def some_function do
+ # call values(orddict)
+ end
+end
+{% endhighlight %}
In this case, we are importing only the function `values` (with arity 1) from `Orddict`. Although `only:` is optional, its usage is recommended. `except` could also be given as an option.
If we want to import only `:functions` or `:macros` from a given module, we can also pass a first argument selecting the scope:
- import :macros, MyMacros
+{% highlight elixir %}
+import :macros, MyMacros
+{% endhighlight %}
And then we can use `only` or `except` to filter the macros being included. Finally, note that `import` is **lexical**, this means we can import specific macros inside specific functions:
- defmodule Math do
- def some_function do
- import Orddict, only: [values: 1]
- # call values(orddict)
- end
- end
+{% highlight elixir %}
+defmodule Math do
+ def some_function do
+ import Orddict, only: [values: 1]
+ # call values(orddict)
+ end
+end
+{% endhighlight %}
In the example above, we imported `Orddict.values` only for during that specific function. `values` won't be available in any other functions in that module.
@@ -148,21 +168,27 @@ In the example above, we imported `Orddict.values` only for during that specific
`require` is responsible to enforce that a module is loaded and to setup references aliases for a given module. For instance, one can do:
- defmodule Math do
- require MyOrddict, as: Orddict
- end
+{% highlight elixir %}
+defmodule Math do
+ require MyOrddict, as: Orddict
+end
+{% endhighlight %}
And now, any reference to `Orddict` will be automatically replaced by `MyOrddict`. In case one wants to access the original `Orddict`, it can be done by prefixing the module name with `::`:
- Orddict.values #=> uses ::MyOrddict.values
- ::Orddict.values #=> uses ::Orddict.values
+{% highlight elixir %}
+Orddict.values #=> uses ::MyOrddict.values
+::Orddict.values #=> uses ::Orddict.values
+{% endhighlight %}
In general, a module does not need to be required before usage, except if we want to use the macros available in that module. For instance, suppose we created our own `my_if` implementation in a module named `MyMacros`. If we want to invoke it, we need to first explicitly require `MyMacros`:
- defmodule Math do
- require MyMacros
- MyMacros.my_if do_something, it_works
- end
+{% highlight elixir %}
+defmodule Math do
+ require MyMacros
+ MyMacros.my_if do_something, it_works
+end
+{% endhighlight %}
An attempt to call a macro that was not loaded will raise an error. Note that, as the import directive, `require` is lexical.
@@ -170,16 +196,20 @@ An attempt to call a macro that was not loaded will raise an error. Note that, a
Elixir also allows module to store their own data. The canonical example for such data is annotating that a module implements the OTP behavior called `gen_server`:
- defmodule MyServer do
- @behavior :gen_server
- # ... callbacks ...
- end
+{% highlight elixir %}
+defmodule MyServer do
+ @behavior :gen_server
+ # ... callbacks ...
+end
+{% endhighlight %}
Now if the module above does not implement any of the callbacks required by `gen_server`, a warning will be raised. Another data used internally by Elixir is is the `@vsn`:
- defmodule MyServer do
- @vsn 2
- end
+{% highlight elixir %}
+defmodule MyServer do
+ @vsn 2
+end
+{% endhighlight %}
`@vsn` refers to version and is used by the code reloading mechanism to check if a module is updated or not. If no version is specified, the version is set to the MD5 of the module functions.
@@ -202,14 +232,18 @@ The following are also reserved by Elixir (as they have special semantics to the
Besides the built-in data above, any developer can also add custom data:
- defmodule MyServer do
- @my_data 13
- IO.inspect @my_data #=> 13
- end
+{% highlight elixir %}
+defmodule MyServer do
+ @my_data 13
+ IO.inspect @my_data #=> 13
+end
+{% endhighlight %}
After the module is compiled, the stored custom data can be accessed via `__info__(:data)` and it will return an `Orddict`:
- MyServer.__info__(:data) #=> [my_data: 13]
+{% highlight elixir %}
+MyServer.__info__(:data) #=> [my_data: 13]
+{% endhighlight %}
> Note: Erlang developers may be wondering why Elixir provides its own data abstraction instead of using Erlang attributes. Erlang attributes are basically a list which also allow duplicated entries. For Elixir, since the same data may be read and updated several times during compilation, it makes more sense to have a dictionary structure instead of a list. Erlang developers wishing to have the attributes functionality have two options:
>
@@ -221,10 +255,12 @@ After the module is compiled, the stored custom data can be accessed via `__info
In Elixir, nesting a module inside the other does not affect its name:
- defmodule Foo do
- defmodule Bar do
- end
- end
+{% highlight elixir %}
+defmodule Foo do
+ defmodule Bar do
+ end
+end
+{% endhighlight %}
The example above will define two modules `Foo` and `Bar`. Notice that the second module is **not** called `Foo::Bar`. In general, nesting modules is discouraged in Elixir.
@@ -232,38 +268,50 @@ The example above will define two modules `Foo` and `Bar`. Notice that the secon
In Erlang (and consequently in the Erlang VM), modules and functions are represented by atoms. For instance, this is valid Erlang code:
- Mod = lists,
- Mod:flatten([1,[2],3]).
+{% highlight elixir %}
+Mod = lists,
+Mod:flatten([1,[2],3]).
+{% endhighlight %}
In the example above, we store the atom `lists` in the variable `Mod` and then invoked the function flatten in it. In Elixir, exactly the same idiom is allowed. In fact, we could call the same function `flatten` in `lists` as:
- iex> :lists.flatten([1,[2],3])
- [1,2,3]
+{% highlight iex %}
+iex> :lists.flatten([1,[2],3])
+[1,2,3]
+{% endhighlight %}
This mechanism is exactly what empowers Elixir references. Elixir references are uppercase identifiers (like `List`, `Orddict`, etc) that are converted to an atom representing a module at compilation time. For instance, by default `List` translates to the atom `::List`:
- iex> List
- ::List
- iex> is_atom(List)
- true
+{% highlight iex %}
+iex> List
+::List
+iex> is_atom(List)
+true
+{% endhighlight %}
References are powerful when used with the `require` directive discussed above. For instance, let's imagine our Math module relies heavily on the `Orddict` module. If, at some point, we find out most algorithms in `Orddict` could be implemented in a much faster way, we could implement `FastOrddict` and use it as a drop-in replacement:
- defmodule Math do
- require FastOrddict, as: Orddict
- # ...
- end
+{% highlight elixir %}
+defmodule Math do
+ require FastOrddict, as: Orddict
+ # ...
+end
+{% endhighlight %}
Now any reference to `Orddict` will be automatically replaced by `FastOrddict`. In case one wants to access the original `Orddict`, it can be done by prefixing the module name with `::`:
- Orddict.values #=> uses ::FastOrddict.values
- ::Orddict.values #=> uses ::Orddict.values
+{% highlight elixir %}
+Orddict.values #=> uses ::FastOrddict.values
+::Orddict.values #=> uses ::Orddict.values
+{% endhighlight %}
Finally, in Elixir `::` is simply an operator (like `+`). It is used to concatenate two references:
- iex> Foo::Bar
- ::Foo::Bar
- iex> Foo :: Bar
- ::Foo::Bar
+{% highlight iex %}
+iex> Foo::Bar
+::Foo::Bar
+iex> Foo :: Bar
+::Foo::Bar
+{% endhighlight %}
-> Note: a reference does not actually ensure the reference really exists. For instance, `::FooBarBaz` will return an atom regardless if a `::FooBarBaz` module is defined or not.
\ No newline at end of file
+> Note: a reference does not actually ensure the reference really exists. For instance, `::FooBarBaz` will return an atom regardless if a `::FooBarBaz` module is defined or not.
diff --git a/getting_started/4.markdown b/getting_started/4.markdown
index 717a5014c..c4c12c525 100644
--- a/getting_started/4.markdown
+++ b/getting_started/4.markdown
@@ -13,45 +13,59 @@ Elixir provides both protocols and records. This section will outline the main f
Records are simple structures that holds values. For example, we can define a `FileInfo` record that is supposed to store information about files as follow:
- defrecord FileInfo, atime: nil, mtime: nil, accesses: 0
+{% highlight elixir %}
+defrecord FileInfo, atime: nil, mtime: nil, accesses: 0
+{% endhighlight %}
The line above will define a module named `FileInfo` which contain a function named `new` that returns a new record and other functions to read and set the values in the record. Therefore, we can do:
- file_info = FileInfo.new(atime: now())
- file_info.atime #=> Returns the value of atime
- file_info.atime(now()) #=> Updates the value of atime
+{% highlight elixir %}
+file_info = FileInfo.new(atime: now())
+file_info.atime #=> Returns the value of atime
+file_info.atime(now()) #=> Updates the value of atime
+{% endhighlight %}
Elixir will also define a `update_#{field}` function that accepts a function as argument that will receive the old value and update the current value with the result of the function:
- file_info = FileInfo.new(accesses: 10)
- file_info = file_info.update_accesses(fn(x) -> x + 1 end)
- file_info.accesses #=> 11
+{% highlight elixir %}
+file_info = FileInfo.new(accesses: 10)
+file_info = file_info.update_accesses(fn(x) -> x + 1 end)
+file_info.accesses #=> 11
+{% endhighlight %}
Internally, a record is simply a tuple where the first element is always the record module name. This can be noticed if we create and print the record in Interactive Elixir (`bin/iex`):
- iex> defrecord FileInfo, atime: nil, mtime: nil
- iex> FileInfo.new
- {::FileInfo, nil, nil}
+{% highlight iex %}
+iex> defrecord FileInfo, atime: nil, mtime: nil
+iex> FileInfo.new
+{::FileInfo, nil, nil}
+{% endhighlight %}
### 4.1.1 Default based functions
Depending on the default value, Elixir will define helpers to interact with the record. For example, the test framework that ships with Elixir, called ExUnit, defines a record which keeps track of how many tests were executed and the failures that happened. The record definition is similar to:
- iex> defrecord Config, counter: 0, failures: []
+{% highlight iex %}
+iex> defrecord Config, counter: 0, failures: []
+{% endhighlight %}
Since `counter` is an integer, Elixir automatically defines a helper named `increment_counter` that will increase the counter value:
- iex> new_config = Config.new.increment_counter
- {::Config, 1, []}
- iex> new_config.counter
- 1
+{% highlight iex %}
+iex> new_config = Config.new.increment_counter
+{::Config, 1, []}
+iex> new_config.counter
+1
+{% endhighlight %}
`increment_counter` also accepts a number to increment as argument:
- iex> new_config = Config.new.increment_counter 10
- {::Config, 10, []}
- iex> new_config.counter
- 10
+{% highlight iex %}
+iex> new_config = Config.new.increment_counter 10
+{::Config, 10, []}
+iex> new_config.counter
+10
+{% endhighlight %}
On the other hand, if the default value is a list Elixir will define the two following helpers:
@@ -66,27 +80,31 @@ In Elixir, only `false` and `nil` are considered falsy values. Everything else e
We could implement this protocol as follow:
- defprotocol Blank, [ blank?(data) ]
+{% highlight elixir %}
+defprotocol Blank, [ blank?(data) ]
+{% endhighlight %}
The protocol expects a function called `blank?` expecting one argument to be implemented. We can implement this protocol for some Elixir data types as follow:
- # Numbers are never blank
- defimpl Blank, for: Number do
- def blank?(number), do: false
- end
-
- # Just empty list is blank
- defimpl Blank, for: List do
- def blank?([]), do: true
- def blank?(_), do: false
- end
-
- # Just the atoms false and nil are blank
- defimpl Blank, for: Atom do
- def blank?(false), do: true
- def blank?(nil), do: true
- def blank?(_), do: false
- end
+{% highlight elixir %}
+# Numbers are never blank
+defimpl Blank, for: Number do
+ def blank?(number), do: false
+end
+
+# Just empty list is blank
+defimpl Blank, for: List do
+ def blank?([]), do: true
+ def blank?(_), do: false
+end
+
+# Just the atoms false and nil are blank
+defimpl Blank, for: Atom do
+ def blank?(false), do: true
+ def blank?(nil), do: true
+ def blank?(_), do: false
+end
+{% endhighlight %}
And we would do so for all native data types. The types available are:
@@ -106,13 +124,17 @@ And we would do so for all native data types. The types available are:
Implementing the protocol for all 9 types above can be cumbersome. Even more if you consider that Number, Function, PID, Port and Reference are never going to be blank. For this reason, Elixir allows us to declare that we are going to implement the protocol just for some types, as follows:
- defprotocol Blank, [blank?(data)], only: [Atom, Tuple, List, BitString, Any]
+{% highlight elixir %}
+defprotocol Blank, [blank?(data)], only: [Atom, Tuple, List, BitString, Any]
+{% endhighlight %}
Since we also specified `Any` as a data type, if the data type is not any of Atom, Tuple, List or BitString, it will automatically fallback to Any:
- defimpl Blank, for: Any do
- def blank?(_), do: false
- end
+{% highlight elixir %}
+defimpl Blank, for: Any do
+ def blank?(_), do: false
+end
+{% endhighlight %}
Now all data types that we have not specified will be automatically considered non blank.
@@ -120,9 +142,11 @@ Now all data types that we have not specified will be automatically considered n
The real benefit of protocols comes when mixed with records. For instance, one may implement a custom dictionary as a Red-Black tree and this dictionary should also be considered blank in case it has no items. That said, the developer just needs to implement the protocol for this dictionary:
- defimpl Blank, for: RedBlack::Dict do
- def blank?(dict), do: RedBlack.empty?(dict)
- end
+{% highlight elixir %}
+defimpl Blank, for: RedBlack::Dict do
+ def blank?(dict), do: RedBlack.empty?(dict)
+end
+{% endhighlight %}
In the example above, we have implemented `blank?` for the custom dictionary that simply delegates to `RedBlack.empty?`. Finally, since records are simply tuples, the default implementation for records can be given in the tuple implementation.
@@ -135,4 +159,4 @@ Elixir ships with three built-in protocols, they are:
* List::Chars - specifies how to convert a data structures with characters to lists
* String::Inspect - specifies how to convert any data structure to a string for inspection
-You can check the source code of those files for more information about how the protocol is used and how to implement your own. With this, we have finally finished this section which has described `defrecord`, `defprotocol` and `defimpl`. Next, we are going to discuss macros with `defmacro`!
\ No newline at end of file
+You can check the source code of those files for more information about how the protocol is used and how to implement your own. With this, we have finally finished this section which has described `defrecord`, `defprotocol` and `defimpl`. Next, we are going to discuss macros with `defmacro`!
diff --git a/getting_started/5.markdown b/getting_started/5.markdown
index f2446ecca..737180d9e 100644
--- a/getting_started/5.markdown
+++ b/getting_started/5.markdown
@@ -13,11 +13,15 @@ Elixir is an homoiconic language. Any Elixir program can be represented using it
The building block of Elixir homoiconicity is a tuple with three elements, for example:
- { :sum, 1, [1, 2, 3] }
+{% highlight elixir %}
+{ :sum, 1, [1, 2, 3] }
+{% endhighlight %}
The tuple above represents a function call to sum passing 1, 2 and 3 as arguments. The tuple elements are:
- { Tuple | Atom, Integer, List | Atom }
+{% highlight elixir %}
+{ Tuple | Atom, Integer, List | Atom }
+{% endhighlight %}
* The first element of the tuple is always an atom or another tuple in the same representation;
* The second element of the tuple is always an integer representing the line number;
@@ -25,26 +29,34 @@ The tuple above represents a function call to sum passing 1, 2 and 3 as argument
You can get the representation of any expression by using the quote macro:
- iex> quote do: sum(1, 2, 3)
- { :sum, 0, [1, 2, 3] }
+{% highlight iex %}
+iex> quote do: sum(1, 2, 3)
+{ :sum, 0, [1, 2, 3] }
+{% endhighlight %}
Everything in Elixir is a function call and can be represented by such tuples. For example, operators are represented as such:
- iex> quote do: 1 + 2
- { :"+", 0, [1, 2] }
+{% highlight iex %}
+iex> quote do: 1 + 2
+{ :"+", 0, [1, 2] }
+{% endhighlight %}
Even a tuple is represented as a call to `{}`:
- iex> quote do: { 1, 2, 3 }
- { :"{}", 0, [1, 2, 3] }
+{% highlight iex %}
+iex> quote do: { 1, 2, 3 }
+{ :"{}", 0, [1, 2, 3] }
+{% endhighlight %}
The only exception to this rule are the five Elixir literals below. Literals are data types that when quoted return themselves. They are:
- :sum #=> Atoms
- 1.0 #=> Numbers
- [1,2] #=> Lists
- "binaries" #=> Binaries
- {key, value} #=> Key-value pairs (i.e. a tuple with two elements)
+{% highlight elixir %}
+:sum #=> Atoms
+1.0 #=> Numbers
+[1,2] #=> Lists
+"binaries" #=> Binaries
+{key, value} #=> Key-value pairs (i.e. a tuple with two elements)
+{% endhighlight %}
With those basic structures in mind, we are ready to define our own macro.
@@ -52,46 +64,62 @@ With those basic structures in mind, we are ready to define our own macro.
A macro can be define using `defmacro`. For instance, we can define a macro called `unless` which works the same as Ruby's unless in just few lines of code:
- defmodule MyMacro do
- defmacro unless(clause, options) do
- quote do: if(!unquote(clause), unquote(options))
- end
- end
+{% highlight elixir %}
+defmodule MyMacro do
+ defmacro unless(clause, options) do
+ quote do: if(!unquote(clause), unquote(options))
+ end
+end
+{% endhighlight %}
Similarly to `if`, `unless` expects two arguments: a `clause` and `options`:
- require MyMacro
- MyMacro.unless var, do: IO.puts "false"
+{% highlight elixir %}
+require MyMacro
+MyMacro.unless var, do: IO.puts "false"
+{% endhighlight %}
However, since `unless` is a macro, it won't receive values when invoked, but instead, its expressions. For example, if one calls:
- unless 2 + 2 == 5, do: call_function()
+{% highlight elixir %}
+unless 2 + 2 == 5, do: call_function()
+{% endhighlight %}
Our `unless` macro will receive the following:
- unless({:==, 1, [{:+, 1, [2, 2]}, 5]}, { :call_function, 1, [] })
+{% highlight elixir %}
+unless({:==, 1, [{:+, 1, [2, 2]}, 5]}, { :call_function, 1, [] })
+{% endhighlight %}
Then our `unless` macro will call `quote`, to return a tree representation of the `if` clause. This means we are transforming our `unless` in a `if`!
There is a common mistake when quoting expressions which is that developers usually forget to `unquote` the proper expression. In order to understand what `unquote` does, let's simply remove it:
- defmacro unless(clause, options) do
- quote do: if(!clause, options)
- end
+{% highlight elixir %}
+defmacro unless(clause, options) do
+ quote do: if(!clause, options)
+end
+{% endhighlight %}
When called as `unless 2 + 2 == 5, do: call_function()`, our `unless` would then literally return:
- if(!clause, options)
+{% highlight elixir %}
+if(!clause, options)
+{% endhighlight %}
Which would fail because the clause and options variables are not defined in the current scope. If we add `unquote` back:
- defmacro unless(clause, options) do
- quote do: if(!unquote(clause), unquote(options))
- end
+{% highlight elixir %}
+defmacro unless(clause, options) do
+ quote do: if(!unquote(clause), unquote(options))
+end
+{% endhighlight %}
Which will then return:
- if(!(2 + 2 == 5), do: call_function())
+{% highlight elixir %}
+if(!(2 + 2 == 5), do: call_function())
+{% endhighlight %}
In other words, unquote is a mechanism to inject expressions into the tree being quoted and is essential to the meta-programming mechanism. Elixir also provides `unquote_splicing` allowing us to inject many expressions at once.
@@ -101,57 +129,67 @@ We can define any macro we want, including override the built-in macros provided
Elixir macros follow Scheme conventions and are hygienic. This means a variable defined inside a macro won't conflict with a variable defined in the context the macro is inserted. For example:
- defmodule Hygiene do
- defmacro no_interference do
- quote do: a = 1
- end
- end
+{% highlight elixir %}
+defmodule Hygiene do
+ defmacro no_interference do
+ quote do: a = 1
+ end
+end
- require Hygiene
+require Hygiene
- a = 13
- Hygiene.no_interference
- a # => 13
+a = 13
+Hygiene.no_interference
+a # => 13
+{% endhighlight %}
In the example above, even if the macro injects `a = 1`, it does not affect the variable `a`. In case the macro wants to explicitly affect the context, it can use `var!`:
- defmodule Hygiene do
- defmacro interference do
- quote do: var!(a) = 1
- end
- end
+{% highlight elixir %}
+defmodule Hygiene do
+ defmacro interference do
+ quote do: var!(a) = 1
+ end
+end
- require Hygiene
+require Hygiene
- a = 13
- Hygiene.interference
- a # => 1
+a = 13
+Hygiene.interference
+a # => 1
+{% endhighlight %}
Macros hygiene only works because Elixir marks a variable as coming from the quote. For example, consider this:
- iex> quote do: x
- { :x, 0, :quoted }
+{% highlight iex %}
+iex> quote do: x
+{ :x, 0, :quoted }
+{% endhighlight %}
Notice that the third element is `:quoted`. It means that x may be a function call with 0 arguments or a variable coming from a quote. On the other hand, an unquoted variable would have the third element equals to nil. Let's consider this final example:
- defmodule Hygiene do
- defmacro quoted(x) do
- quote do
- { unquote(x), x, x() }
- end
- end
+{% highlight elixir %}
+defmodule Hygiene do
+ defmacro quoted(x) do
+ quote do
+ { unquote(x), x, x() }
end
+ end
+end
+{% endhighlight %}
In the example above, we have defined a macro called `quoted` that returns an unquoted variable, a quoted variable and a function call. Calling this macro will return:
- require Hygiene
+{% highlight elixir %}
+require Hygiene
- Hygiene.quoted(x)
- #=> {
- { :x, 1, nil },
- { :x, 1, :quoted },
- { :x, 1, [] }
- }
+Hygiene.quoted(x)
+#=> {
+ { :x, 1, nil },
+ { :x, 1, :quoted },
+ { :x, 1, [] }
+}
+{% endhighlight %}
Summing up: if the third element is a list, it is certainly a function call. If not, it may be a variable (coming from a quote or not) or a function call.
@@ -159,26 +197,30 @@ Summing up: if the third element is a list, it is certainly a function call. If
In order to support recursion, macros cannot be called locally. For example, one cannot write:
- defmodule MyMacros
- defmacro delegate([h|t], to: target) do
- # ...
- end
+{% highlight elixir %}
+defmodule MyMacros
+ defmacro delegate([h|t], to: target) do
+ # ...
+ end
- # Call the macro delegate just defined above
- delegate [values: 1], to: List
- end
+ # Call the macro delegate just defined above
+ delegate [values: 1], to: List
+end
+{% endhighlight %}
In order to access the macro, it needs to be defined in an outer module:
- defmodule MyMacros::Support
- defmacro delegate([h|t], to: target) do
- # ...
- end
- end
+{% highlight elixir %}
+defmodule MyMacros::Support
+ defmacro delegate([h|t], to: target) do
+ # ...
+ end
+end
- defmodule MyMacros
- require MyMacros::Support, import: true
- delegate [values: 1], to: List
- end
+defmodule MyMacros
+ require MyMacros::Support, import: true
+ delegate [values: 1], to: List
+end
+{% endhighlight %}
-With this note, we finish our introduction to macros. Next, let's move to the next chapter which will discuss several topics as native code compilation, partial application and others.
\ No newline at end of file
+With this note, we finish our introduction to macros. Next, let's move to the next chapter which will discuss several topics as native code compilation, partial application and others.
diff --git a/getting_started/6.markdown b/getting_started/6.markdown
index f9778b201..8e7d2f3e0 100644
--- a/getting_started/6.markdown
+++ b/getting_started/6.markdown
@@ -11,21 +11,27 @@ guide: 6
Elixir uses the module data described in chapter 3 to drive its documentation system. For instance, consider the following example:
- defmodule MyModule do
- @moduledoc "It does X"
+{% highlight elixir %}
+defmodule MyModule do
+ @moduledoc "It does X"
- @doc "Returns the version"
- def version, do: 1
- end
+ @doc "Returns the version"
+ def version, do: 1
+end
+{% endhighlight %}
In the example above, we are adding a module documentation to MyModule via `@moduledoc` and using `@doc` to document each function. When compiled with the `--docs` option, we will be able to inspect the documentation attributes in runtime (remember to start iex in the same directory you compiled the module):
- $ elixirc my_module.ex --docs
- $ iex
- iex> MyModule.__info__(:docs)
- [{ { :version, 0 }, 5, :def, "Returns the version" }]
- iex> MyModule.__info__(:moduledoc)
- {1,"It does X"}
+{% highlight console %}
+$ elixirc my_module.ex --docs
+$ iex
+{% endhighlight %}
+{% highlight iex %}
+iex> MyModule.__info__(:docs)
+[{ { :version, 0 }, 5, :def, "Returns the version" }]
+iex> MyModule.__info__(:moduledoc)
+{1,"It does X"}
+{% endhighlight %}
`__info__(:docs)` returns a list of tuples where each tuple contains the function/arity pair, the line the function was defined, the kind of the function (`def` or `defmacro`, docs applied to `defp` are always ignored) and the comments. The comment should be either a binary or a boolean.
@@ -33,23 +39,25 @@ Similarly, `__info__(:moduledoc)` returns a tuple with the line the module was d
In case `--docs` is not provided during compilation, both calls would return nil. Elixir promotes the use of markdown in documentation, since it is a widely available format. Consider for example the documentation for `Module.add_doc` which allows us to dynamically add a documentation to a function:
- @doc """
- Attaches documentation to a given function. It expects
- the module the function belongs to, the line (a non negative
- integer), the kind (`:def` or `:defmacro`), a tuple representing
- the function and its arity and the documentation, which should
- be either a binary or a boolean.
+{% highlight elixir %}
+@doc """
+Attaches documentation to a given function. It expects
+the module the function belongs to, the line (a non negative
+integer), the kind (`:def` or `:defmacro`), a tuple representing
+the function and its arity and the documentation, which should
+be either a binary or a boolean.
- ## Examples
+## Examples
- defmodule MyModule do
- Module.add_doc(__MODULE__, __LINE__ + 1,
- :def, { :version, 0}, "Manually added docs")
- def version, do: 1
- end
+ defmodule MyModule do
+ Module.add_doc(__MODULE__, __LINE__ + 1,
+ :def, { :version, 0}, "Manually added docs")
+ def version, do: 1
+ end
- """
- def add_doc(module, line, kind, tuple, doc)
+"""
+def add_doc(module, line, kind, tuple, doc)
+{% endhighlight %}
In the example, we use heredocs to allow the documentation to spawn several lines and markdown to style the documentation.
@@ -57,22 +65,28 @@ In the example, we use heredocs to allow the documentation to spawn several line
Elixir also supports partial application. Let's suppose we have a list of strings and we want to calculate the size for each them. We could do it as follow:
- iex> list = ["foo", "bar", "baz"]
- ["foo","bar","baz"]
- iex> Enum.map list, fn(x) -> size(x) end
- [3,3,3]
+{% highlight iex %}
+iex> list = ["foo", "bar", "baz"]
+["foo","bar","baz"]
+iex> Enum.map list, fn(x) -> size(x) end
+[3,3,3]
+{% endhighlight %}
However, with partial application, we could also do:
- iex> Enum.map list, size(&1)
- [3,3,3]
+{% highlight iex %}
+iex> Enum.map list, size(&1)
+[3,3,3]
+{% endhighlight %}
In the example above, we have invoked the function `size` passing `&1` as argument asking Elixir to generate a function that expects one argument and that argument will be passed to `size`.
Since operators are also function calls they can also be partially applied:
- iex> Enum.map [1,2,3], &1 * 2
- [2,4,6]
+{% highlight iex %}
+iex> Enum.map [1,2,3], &1 * 2
+[2,4,6]
+{% endhighlight %}
All functions can be partially applied, except [Elixir's special forms](https://github.com/elixir-lang/elixir/tree/master/lib/elixir/special_forms.ex).
@@ -80,24 +94,28 @@ All functions can be partially applied, except [Elixir's special forms](https://
`use` is a macro intended to a common API for extension. For instance, in order to use the `ExUnit` test framework that ships with Elixir, you simply need to use `ExUnit::Case` in your module:
- defmodule AssertionTest do
- use ExUnit::Case
+{% highlight elixir %}
+defmodule AssertionTest do
+ use ExUnit::Case
- def test_always_pass do
- true = true
- end
- end
+ def test_always_pass do
+ true = true
+ end
+end
+{% endhighlight %}
By calling `use`, a hook called `__using__` will be invoked in `ExUnit::Case` which will then do the proper setup. In general, `use` is simply a translation to:
- defmodule AssertionTest do
- require ExUnit::Case
- ExUnit::Case.__using__(::AssertionTest)
+{% highlight elixir %}
+defmodule AssertionTest do
+ require ExUnit::Case
+ ExUnit::Case.__using__(::AssertionTest)
- def test_always_pass do
- true = true
- end
- end
+ def test_always_pass do
+ true = true
+ end
+end
+{% endhighlight %}
In general, we recommend APIs to expose a `__using__` hook in case they want to expose functionality to developers.
@@ -105,61 +123,75 @@ In general, we recommend APIs to expose a `__using__` hook in case they want to
Elixir also provides list and bit comprehensions. List comprehensions allow you to quickly build a list from another list:
- iex> lc n in [1,2,3,4], do: n * 2
- [2,4,6,8]
+{% highlight iex %}
+iex> lc n in [1,2,3,4], do: n * 2
+[2,4,6,8]
+{% endhighlight %}
Or, using key-value blocks:
- lc n in [1,2,3,4] do
- n * 2
- end
+{% highlight elixir %}
+lc n in [1,2,3,4] do
+ n * 2
+end
+{% endhighlight %}
A comprehension accepts several expressions. Those expressions can be generators, as in `x in [1,2,3,4]`, or filters:
- # A comprehension with a generator and a filter
- iex> lc n in [1,2,3,4,5,6], rem(n, 2) == 0, do: n
- [2,4,6]
+{% highlight iex %}
+# A comprehension with a generator and a filter
+iex> lc n in [1,2,3,4,5,6], rem(n, 2) == 0, do: n
+[2,4,6]
- # A comprehension with two generators
- iex> lc x in [1,2], y in [2,3], do: x*y
- [2,3,4,6]
+# A comprehension with two generators
+iex> lc x in [1,2], y in [2,3], do: x*y
+[2,3,4,6]
+{% endhighlight %}
Elixir provides generators for both lists and bitstrings:
- # A list generator:
- iex> lc n in [1,2,3,4], do: n * 2
- [2,4,6,8]
+{% highlight iex %}
+# A list generator:
+iex> lc n in [1,2,3,4], do: n * 2
+[2,4,6,8]
- # A bit string generator:
- iex> lc <> in <<1,2,3,4>>, do: n * 2
- [2,4,6,8]
+# A bit string generator:
+iex> lc <> in <<1,2,3,4>>, do: n * 2
+[2,4,6,8]
+{% endhighlight %}
Bit string generators are quite useful when you need to organize bit string streams:
- iex> pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>
- iex> lc <> in pixels, do: {r,g,b}
- [{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
+{% highlight iex %}
+iex> pixels = <<213,45,132,64,76,32,76,0,0,234,32,15>>
+iex> lc <> in pixels, do: {r,g,b}
+[{213,45,132},{64,76,32},{76,0,0},{234,32,15}]
+{% endhighlight %}
Remember, as strings are binaries and a binary is a bitstring, we can also use strings on comprehensions. For instance, the example below removes all white space characters from a string via bit comprehensions:
- iex> bc <> in " hello world ", c != ?\s, do: <>
- "helloworld"
+{% highlight iex %}
+iex> bc <> in " hello world ", c != ?\s, do: <>
+"helloworld"
+{% endhighlight %}
Elixir does its best to hide the differences between list and bit string generators. However, there is a special case due to Erlang limitation that we need to explicitly tell Erlang that a list is being given as argument:
- # This will fail because when Elixir sees that the left side
- # of the in expression is a bit string, it expects the right side
- # to be a bit string as well:
- iex> lc <> in [<<1>>,<<2>>,<<3>>], do: n*2
- ** (ErlangError) erlang error {:bad_generator,[<<1>>,<<2>>,<<3>>]}
-
- # You need to be explicit and use inlist:
- iex> lc inlist(<>, [<<1>>,<<2>>,<<3>>]), do: n*2
- [2,4,6]
-
- # For consistency, inbin is also available:
- iex> lc inbin(<>, <<1,2,3>>), do: n*2
- [2,4,6]
+{% highlight iex %}
+# This will fail because when Elixir sees that the left side
+# of the in expression is a bit string, it expects the right side
+# to be a bit string as well:
+iex> lc <> in [<<1>>,<<2>>,<<3>>], do: n*2
+** (ErlangError) erlang error {:bad_generator,[<<1>>,<<2>>,<<3>>]}
+
+# You need to be explicit and use inlist:
+iex> lc inlist(<>, [<<1>>,<<2>>,<<3>>]), do: n*2
+[2,4,6]
+
+# For consistency, inbin is also available:
+iex> lc inbin(<>, <<1,2,3>>), do: n*2
+[2,4,6]
+{% endhighlight %}
## 6.5 Native compilation