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
-[Trying It All Together](#trying-it-all-together)
20
+
-[Macro Rules](#macro-rules)
21
+
-[The Abstract Syntax Tree - Demystified](#the-abstract-syntax-tree-demystified)
22
+
-[High-Level Syntax vs. Low-level AST](#high-level-syntax-vs-low-level-ast)
23
+
-[AST Literals](#ast-literals)
24
+
-[Macros: The Building Blocks of Elixir](#macros-the-building-blocks-of-elixir)
20
25
21
26
### Chapter 1 - The Language of Macros
22
27
@@ -71,7 +76,7 @@ iex> quote do: div(10, 2)
71
76
In most languages, we would have to parse a string expression into something digestible by our program. With Elixir, we can access
72
77
the representation of expressions directly with macros."
73
78
74
-
[First macro - `math.exs`](math.exs)
79
+
[First macro - `math.exs`](math/math.exs)
75
80
76
81
<details>
77
82
<summary>math.exs</summary>
@@ -144,7 +149,168 @@ Which is not the expected result. For this, we use unquote:
144
149
I assume from this then when you pass variables to a macro they need to be `unqoute`-d, incontrast to passing a value directly.
145
150
Which I'm not following as Elixir is pass-by-value so wouldn't the value just be known?
146
151
147
-
Yes that's correct because we are dealing with ASTs not the data it represents; therefore the pass-by-value argument doesn't hold.
152
+
Turns out that yes that's correct because we are dealing with ASTs not the data it represents; therefore the pass-by-value argument doesn't hold.
148
153
Much like interpolation from Ecto and the difference between `"Hello world"` and `"Hello #{world}`.
149
154
150
-
Back to the `math.exs`example.
155
+
- We know that macros receive the AST representation of the arguments.
156
+
- "To complete the macro, we used `quote` to return an AST for the caller to replace out `Math.say` invocations." I'm assuming by this that every macro needs some form of `quote`.
157
+
158
+
#### Macro Rules
159
+
160
+
-**Rule 1: Don't write macros.** We have to remember that writing code to produce code require special care. It's easy to get caught in a web of your own code generation. Too many macros can make debugging more difficult.
161
+
162
+
-**Rule: 2 Use macros gratuitously.** Metaprogramming is sometimes framed as complex and fragile as well as offering productive advantages in a fraction of the required code. It's important to keep this duality in mind when writing macros.
163
+
164
+
#### The Abstract Syntax Tree - Demystified
165
+
166
+
- Every expression you write in Elixir breaks down to a three-element tuple in the AST.
167
+
- This uniform format makes pattern matching arguments a lot easier.
168
+
- Quoting a couple more complex expressions to see how entire Elixir prograns are structure in the AST.
- A stacking tuple was produced from each quoted exoression. The first example shows the familiar structures used by our `Math.say` macro, but multiple tuples are stacked into an embedded tree to represent the entire expression. The result of the second example shows how an entire Eliir module represented by a simple AST.
207
+
208
+
-**All elixir code is represented as a series of three-element tuples with the following format:**
209
+
210
+
-**The first element** is an atom denoting the funcation call, or another tuple, representing a nested node in the AST.
211
+
-**The second element** represents metadata about the expression.
212
+
-**The third element** is a list of arguments for the function call.
213
+
214
+
- Applying this to the AST of `(5 * 2) - 1 + 7` from before
- An AST tree strunction of functions and arguments has been made. If we were to format this output into a more readable tree:
227
+
228
+
```bash
229
+
+
230
+
├── -
231
+
│ ├── *
232
+
│ │ ├── 5
233
+
│ │ └── 7
234
+
│ └── 1
235
+
└── 7
236
+
```
237
+
238
+
- It seems easiest to start from the end of the AST and work up. The root AST node is the `+` operator, and its arguments are
239
+
the number 7 combined with another nested node in the tree. We can see that the nested nodes contain out `(5 * 2)` expression,
240
+
whose results are applied to the `- 1` branch.
241
+
-`5 * 2` is syntactic sugar for `Kernel.*(5, 2)`. This means the `:*` atom is just a function call from the import of `Kernel` as
242
+
you can see from the AST output from before.
243
+
244
+
#### High-Level Syntax vs. Low-level AST
245
+
246
+
Comparing the AST from elixir to the source of Lisp. If you look closely, you can see how elixir operates at a layer just above this format.
247
+
248
+
For example, Lisp source code:
249
+
250
+
```lisp
251
+
(+ (* 2 3) 1)
252
+
```
253
+
254
+
And Elixir from source to generated AST:
255
+
256
+
```elixir
257
+
quotedo:2*3+1
258
+
{:+, _, [{:*, _, [2, 3]}, 1]}
259
+
```
260
+
261
+
If we compare the both you can see that the structure itself is nearly identical with only the syntax being different. The beauty here is that
262
+
the transformation from high-level source to low-level AST requires only a `quote` invocation. On the contrary, with Lisp you have all the power
263
+
of a programmable AST at the cost of a less natural and flexible syntax. José seperated the AST from the syntax, meaning we get the best of both worlds.
264
+
265
+
#### AST Literals
266
+
267
+
- Playing with elixir source, sometimes the results of quoted expressions can appear confusing and irregular. This is because of literals. Literals
268
+
have the same representation within the AST and at high-lvel source. This includes atoms, ints, floats, lists, strings and any two-element tuples
269
+
containing the former types. Such as the following:
270
+
271
+
```elixir
272
+
iex>quotedo::atom
273
+
:atom
274
+
275
+
iex>quotedo:123
276
+
123
277
+
278
+
iex>quotedo: [1, 2, 3]
279
+
280
+
iex>quotedo: {:ok, [1, 2, 3]}
281
+
{:ok, [1, 2, 3]}
282
+
```
283
+
284
+
If you pass any of the example to a macro, the macro receives the literal arguments instead of an abstract representation.
285
+
286
+
```elixir
287
+
quoute do: %{a:1, b:2}
288
+
{:%{}, [], [a:1, b:2]}
289
+
290
+
iex> quoute do:Enum
291
+
{:__aliases__, [alias:false], [:Enum]}
292
+
```
293
+
294
+
In these two examples we see that there are two different ways in which elixir types are represented in the AST. Some values are passed through as
295
+
is, while more complex tuples are returned as a quoted expression. It's useful to keep literals in mind when writing macros to avoid confusion.
296
+
297
+
#### Macros: The Building Blocks of Elixir
298
+
299
+
- Let's imagine that `unless` does not exist in the elixir language. `unless` is essentially the same as a negated if statement, e.g. unless(1 + 1 = 5) would return true.
0 commit comments