You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: getting_started/20.markdown
+137Lines changed: 137 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,3 +5,140 @@ guide: 20
5
5
---
6
6
7
7
# {{page.title }}
8
+
9
+
An Elixir program can be represented by its own data structures. In this chapter, we will learn how those structures look like and how to compose them. The concepts learned in this chapter are the building blocks for macros, which we are going to take a deeper look at the next chapter.
10
+
11
+
## 20.1 Quoting
12
+
13
+
The building block of an Elixir program is a tuple with three elements. For example, the function call `sum(1,2,3)` is represented internally as:
14
+
15
+
```elixir
16
+
{:sum, [], [1, 2, 3]}
17
+
```
18
+
19
+
You can get the representation of any expression by using the `quote` macro:
20
+
21
+
```iex
22
+
iex> quote do: sum(1, 2, 3)
23
+
{:sum, [], [1, 2, 3]}
24
+
```
25
+
26
+
The first element is the function name, the second is a keyword list containing metadata and the third is the arguments list.
27
+
28
+
Operators are also represented as such tuples:
29
+
30
+
```iex
31
+
iex> quote do: 1 + 2
32
+
{:+, [context: Elixir, import: Kernel], [1, 2]}
33
+
```
34
+
35
+
Even a map is represented as a call to `%{}`:
36
+
37
+
```iex
38
+
iex> quote do: %{1 => 2}
39
+
{:%{}, [], [{1, 2}]}
40
+
```
41
+
42
+
Variables are also represented using such triplets, except the last element is an atom, instead of a list:
43
+
44
+
```iex
45
+
iex> quote do: x
46
+
{:x, [], Elixir }
47
+
```
48
+
49
+
When quoting more complex expressions, we can see the code is represented in such tuples, often nested inside each other resembling a tree. Many languages would call those representations the Abstract Syntax Tree (AST). Elixir calls them quoted expressions:
Sometimes when working with quoted expressions, it may be useful to get the textual code representation back. This can be done with `Macro.to_string/1`:
57
+
58
+
```iex
59
+
iex> Macro.to_string(quote do: sum(1, 2 + 3, 4))
60
+
"sum(1, 2 + 3, 4)"
61
+
```
62
+
63
+
In general, the tuples above follow the following format:
64
+
65
+
```elixir
66
+
{tuple | atom, list, list | atom }
67
+
```
68
+
69
+
* The first element is an atom or another tuple in the same representation;
70
+
* The second element is a keyword list containing information like metadata like numbers and contexts;
71
+
* The third element is either a list of arguments for the function call or an atom. When an atom, it means the tuple represents a variable.
72
+
73
+
Besides the tuple defined above, there are five Elixir literals that when quoted return themselves (and not a tuple). They are:
74
+
75
+
```elixir
76
+
:sum#=> Atoms
77
+
1.0#=> Numbers
78
+
[1,2] #=> Lists
79
+
"strings"#=> Strings
80
+
{key, value} #=> Tuples with two elements
81
+
```
82
+
83
+
Most of Elixir code has straight-forward translation to its underlying quoted expression. We recommend you to try out different code samples and see what gets out of it. For example, what `String.upcase("foo")` expands to? We have also learned that `if(true, do: this, else: that)` is the same as `if true do this else that end`. How does this affirmation hold with quoted expressions?
84
+
85
+
## 20.2 Unquoting
86
+
87
+
Quote is about retrieving the inner representation of some particular chunk of code. However, sometimes it may be necessary to inject some other particular chunk of code inside the representation we want to retrieve.
88
+
89
+
For example, imagine you have a variable `number` which contains the number you want to inject inside a quoted expression. The number can be injected into the quoted representation by using `unquote`:
In some cases, it may be necessary to inject many values inside a list. For example, imagine you have a list containing `[1, 2, 6]` and we want to inject `[3, 4, 5]` into it. Using `unquote` won't yield the desired result:
Unquoting is very useful when working with macros. When writing macros, developers are able to receive code chunks and inject them inside other code chunks, being able to transform code or write code that generates code during compilation.
122
+
123
+
## 20.3 Escaping
124
+
125
+
As we have seen at the beginning of this chapter, only some values are valid quoted expressions. For example, a map is not a valid quoted expression. Nor a tuple with three elements. However, we have seen before those values *can* be expressed as a quoted expression:
126
+
127
+
```iex
128
+
iex> quote do: %{1 => 2}
129
+
{:%{}, [], [{1, 2}]}
130
+
```
131
+
132
+
In some cases, you may need to inject such *values* into *quoted expressions*. To do that, we need to first escape those values into quoted expressions with the help of `Macro.escape/1`:
133
+
134
+
```iex
135
+
iex> map = %{hello: :world}
136
+
iex> Macro.escape(map)
137
+
{%{}, [], [hello: :world]}
138
+
```
139
+
140
+
In other words, it is important to make a distinction in between a regular Elixir value (like a list, a map, a process, a reference, etc) and a quoted expression. Some values like integers, atoms and strings have a quoted expression equal to the value itself. Other values like maps needs to be explicitly converted. Finally, some values like functions and references cannot be converted to a quoted expression at all.
141
+
142
+
Macros receive quoted expressions and must return quoted expressions. However, sometimes during the function of a macro, you may need to work with values and making a distinction in between them may be required.
143
+
144
+
With this introduction we have laid the ground to finally write out first macro, so let's move to the next chapter.
0 commit comments