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: docs/src/lectures/lecture_06/lecture.md
+49-45Lines changed: 49 additions & 45 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -318,17 +318,19 @@ and the output is used mainly for debugging / inspection.
318
318
Language introspection is very convenient for investigating, how things are implemented and how they are optimized / compiled to the native code.
319
319
320
320
::: note "Reminder `@which`"
321
-
Though we have already used it quite a few times, recall the very useful macro`@which`, which identifies the concrete function called in a function call. For example `@which mapreduce(sin, +, [1,2,3,4])`. Note again that the macro here is a convenience macro to obtain types of arguments from the expression. Under the hood, it calls `InteractiveUtils.which(function_name, (Base.typesof)(args...))`. Funny enough, you can call `@which InteractiveUtils.which(+, (Base.typesof)(1,1))` to inspect, where`which` is defined.
321
+
Though we have already used it quite a few times, recall the very useful macro`@which`, which identifies the concrete function called in a function call. For example `@which mapreduce(sin, +, [1,2,3,4])`. Note again that the macro here is a convenience macro to obtain types of arguments from the expression. Under the hood, it calls `InteractiveUtils.which(function_name, (Base.typesof)(args...))`. Funny enough, you can call `@which InteractiveUtils.which(+, (Base.typesof)(1,1))` to inspect, where`which` is defined.
322
322
323
-
Alternatively, you can invest time in learning `Cthulhu.jl` package, which is a tool for inspecting functions called in a function. In other words it will simplify a recursive call of which when one is interested in how internals of some function are implemented.
323
+
Alternatively, you can invest time in learning `Cthulhu.jl` package, which is a tool for inspecting functions called in a function. In other words it will simplify a recursive call of which when one is interested in how internals of some function are implemented.
324
+
:::
324
325
325
326
::: note "Effect analysis"
326
-
The compiler is analysing the code to automatically infer some properties, which can help it to create more efficient code. This is analysis id called effect analysis and you can execute it yourself using`Base.infer_effects`. For example for our `nextfib` we obtain (on 1.11.1)
327
-
```julia
328
-
julia> Base.infer_effects(nextfib, (Int,))
329
-
(+c,+e,+n,!t,+s,+m,+u,+o,+r)
330
-
```
331
-
See the documentation for (Base.@assume_effects)[https://docs.julialang.org/en/v1/base/base/#Base.@assume_effects] for details of individual fields.
327
+
The compiler is analysing the code to automatically infer some properties, which can help it to create more efficient code. This is analysis id called effect analysis and you can execute it yourself using`Base.infer_effects`. For example for our `nextfib` we obtain (on 1.11.1)
328
+
```julia
329
+
julia> Base.infer_effects(nextfib, (Int,))
330
+
(+c,+e,+n,!t,+s,+m,+u,+o,+r)
331
+
```
332
+
See the documentation for (Base.@assume_effects)[https://docs.julialang.org/en/v1/base/base/#Base.@assume_effects] for details of individual fields.
333
+
:::
332
334
333
335
### Broadcasting
334
336
Broadcasting is not a unique concept in programming languages (Python/Numpy, MATLAB), however its implementation in Julia allows to easily fuse operations. For example
`Cthulhu.jl` is a library (tool) which simplifies the above, where we want to iteratively dive into functions called in some piece of code (typically some function).`Cthulhu` is different from te normal debugger, since the debugger is executing the code, while`Cthulhu` is just lower_typing the code and presenting functions (with type of arguments inferred).
404
+
::: info Cthulhu.jl
405
+
`Cthulhu.jl` is a library (tool) which simplifies the above, where we want to iteratively dive into functions called in some piece of code (typically some function).`Cthulhu` is different from te normal debugger, since the debugger is executing the code, while`Cthulhu` is just lower_typing the code and presenting functions (with type of arguments inferred).
406
+
:::
405
407
406
408
```julia
407
409
using Cthulhu
@@ -468,57 +470,59 @@ The parsed code `p` is of type `Expr`, which according to Julia's help[^2] is *a
When manipulations of expressions, we encounter the term `Symbol`. `Symbol` is the smallest atom from which the program (in AST representation) is built. It is used to identify an element in the language, for example variable, keyword or function name. Symbol is not a string, since string represents itself, whereas `Symbol` can represent something else (a variable). An illustrative example[^3] goes as follows.
472
-
```julia
473
-
julia> eval(:foo)
474
-
ERROR: foo not defined
473
+
When manipulations of expressions, we encounter the term `Symbol`. `Symbol` is the smallest atom from which the program (in AST representation) is built. It is used to identify an element in the language, for example variable, keyword or function name. Symbol is not a string, since string represents itself, whereas `Symbol` can represent something else (a variable). An illustrative example[^3] goes as follows.
474
+
```julia
475
+
julia> eval(:foo)
476
+
ERROR: foo not defined
475
477
476
-
julia> foo = "hello"
477
-
"hello"
478
+
julia> foo = "hello"
479
+
"hello"
478
480
479
-
julia> eval(:foo)
480
-
"hello"
481
+
julia> eval(:foo)
482
+
"hello"
481
483
482
-
julia> eval("foo")
483
-
"foo"
484
-
```
485
-
which shows that what the symbol `:foo` evaluates to depends on what – if anything – the variable `foo` is bound to, whereas "foo" always just evaluates to "foo".
484
+
julia> eval("foo")
485
+
"foo"
486
+
```
487
+
which shows that what the symbol `:foo` evaluates to depends on what – if anything – the variable `foo` is bound to, whereas "foo" always just evaluates to "foo".
486
488
487
-
Symbols can be constructed either by prepending any string with `:` or by calling `Symbol(...)`, which concatenates the arguments and create the symbol out of it. All of the following are symbols
488
-
```julia
489
-
julia> :+
490
-
:+
489
+
Symbols can be constructed either by prepending any string with `:` or by calling `Symbol(...)`, which concatenates the arguments and create the symbol out of it. All of the following are symbols
Symbols therefore allows us to operate with a piece of code without evaluating it.
506
+
julia> Symbol("Symbol with blanks")
507
+
Symbol("Symbol with blanks")
508
+
```
509
+
Symbols therefore allows us to operate with a piece of code without evaluating it.
508
510
509
-
In Julia, symbols are "interned strings", which means that compiler attaches each string a unique identifier (integer), such that it can quickly compare them. Compiler uses Symbols exclusively and the important feature is that they can be quickly compared. This is why people like to use them as keys in`Dict`.
511
+
In Julia, symbols are "interned strings", which means that compiler attaches each string a unique identifier (integer), such that it can quickly compare them. Compiler uses Symbols exclusively and the important feature is that they can be quickly compared. This is why people like to use them as keys in`Dict`.
512
+
:::
510
513
511
514
[^3]: An [example](https://stackoverflow.com/questions/23480722/what-is-a-symbol-in-julia) provided by Stefan Karpinski.
512
515
513
516
::: info "Expressions"
514
-
From Julia's help[^2]:
517
+
From Julia's help[^2]:
515
518
516
-
`Expr(head::Symbol, args...)`
519
+
`Expr(head::Symbol, args...)`
517
520
518
-
A type representing compound expressions in parsed julia code (ASTs). Each expression consists of a head `Symbol` identifying which kind of expression it is (e.g. a call, for loop, conditional statement, etc.), and subexpressions (e.g. the arguments of a call).
519
-
The subexpressions are stored in a `Vector{Any}` field called args.
521
+
A type representing compound expressions in parsed julia code (ASTs). Each expression consists of a head `Symbol` identifying which kind of expression it is (e.g. a call, for loop, conditional statement, etc.), and subexpressions (e.g. the arguments of a call).
522
+
The subexpressions are stored in a `Vector{Any}` field called args.
520
523
521
-
The expression is simple yet very flexible. The head `Symbol` tells how the expression should be treated and arguments provide all needed parameters. Notice that the structure is also type-unstable. This is not a big deal, since the expression is used to generate code, hence it is not executed repeatedly.
524
+
The expression is simple yet very flexible. The head `Symbol` tells how the expression should be treated and arguments provide all needed parameters. Notice that the structure is also type-unstable. This is not a big deal, since the expression is used to generate code, hence it is not executed repeatedly.
525
+
:::
522
526
523
527
## Construct code from scratch
524
528
Since `Expr` is a Julia structure, we can construct it manually as we can construct any other structure
Copy file name to clipboardExpand all lines: docs/src/lectures/lecture_07/lecture.md
+43-46Lines changed: 43 additions & 46 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -74,22 +74,21 @@ CodeInfo(
74
74
)
75
75
```
76
76
77
-
::: info
78
-
### Scope of eval
79
-
`eval` function is always evaluated in the global scope of the `Module` in which the macro is called (note that there is that by default you operate in the `Main` module). Moreover, `eval` takes effect **after** the function has been has been executed. This can be demonstrated as
80
-
```julia
81
-
add1(x) = x + 1
82
-
function redefine_add(x)
83
-
eval(:(add1(x) = x - 1))
84
-
add1(x)
85
-
end
86
-
julia> redefine_add(1)
87
-
2
88
-
89
-
julia> redefine_add(1)
90
-
0
91
-
92
-
```
77
+
::: info Scope of eval
78
+
`eval` function is always evaluated in the global scope of the `Module` in which it is called (note that there is that by default you operate in the `Main` module). Moreover, `eval` takes effect **after** the function has been has been executed. This can be demonstrated as
79
+
```julia
80
+
add1(x) = x +1
81
+
functionredefine_add(x)
82
+
eval(:(add1(x) = x -1))
83
+
add1(x)
84
+
end
85
+
julia>redefine_add(1)
86
+
2
87
+
88
+
julia>redefine_add(1)
89
+
0
90
+
```
91
+
:::
93
92
94
93
Macros are quite tricky to debug. Macro `@macroexpand` allows to observe the expansion of macros. Observe the effect as
95
94
```julia
@@ -103,7 +102,6 @@ function cosp2(x)
103
102
@replace_sin2+sin(x)
104
103
end
105
104
```
106
-
107
105
First, Julia parses the code into the AST as
108
106
```julia
109
107
ex = Meta.parse("""
@@ -161,7 +159,6 @@ end
161
159
```
162
160
(the `@showarg(1 + 1, :x)` raises an error, since `:(:x)` is of Type `QuoteNode`).
163
161
164
-
165
162
Observe that macro dispatch is based on the types of AST that are handed to the macro, not the types that the AST evaluates to at runtime.
166
163
167
164
List of all defined versions of macro
@@ -228,44 +225,44 @@ end
228
225
The error code snippet errors telling us that the expression `"$"` is outside of a quote block. This is because the macro `@no_quote` has returned a block with `$` occuring outside of `quote` or string definition.
or `@benchmark` support interpolation of values. This interpolation needs to be handled by the logic of the macro and is not automatically handled by Julia language.
or `@benchmark` support interpolation of values. This interpolation needs to be handled by the logic of the macro and is not automatically handled by Julia language.
235
+
:::
238
236
239
237
Macros do not know about runtime values, they only know about syntax trees. When a macro receives an expression with a `$x` in it, it can't interpolate the value of x into the syntax tree because it reads the syntax tree before `x` ever has a value!
240
238
241
239
Instead, when a macro is given an expression with `$` in it, it assumes you're going to give your own meaning to `$x`. In the case of BenchmarkTools.jl they return code that has to wait until runtime to receive the value of `x` and then splice that value into an expression which is evaluated and benchmarked. Nowhere in the actual body of the macro do they have access to the value of `x` though.
242
240
243
241
244
-
::: info
245
-
### Why `$` for interpolation?
246
-
The `$` string for interpolation was used as it identifies the interpolation inside the string and inside the command. For example
247
-
```julia
248
-
a = 5
249
-
s = "a = $(a)"
250
-
typoef(s)
251
-
println(s)
252
-
filename = "/tmp/test_of_interpolation"
253
-
run(`touch $(filename)`)
254
-
```
242
+
::: info Why `$` for interpolation?
243
+
The `$`string for interpolation was used as it identifies the interpolation inside the string and inside the command. For example
244
+
```julia
245
+
a =5
246
+
s ="a = $(a)"
247
+
typoef(s)
248
+
println(s)
249
+
filename ="/tmp/test_of_interpolation"
250
+
run(`touch $(filename)`)
251
+
```
252
+
:::
255
253
256
-
## [Macro hygiene](@id lec7_hygiene)
254
+
## Macro hygiene
257
255
Macro hygiene is a term coined in 1986 addressing the following problem: if you're automatically generating code, it's possible that you will introduce variable names in your generated code that will clash with existing variable names in the scope in which a macro is called. These clashes might cause your generated code to read from or write to variables that you should not be interacting with. A macro is hygienic when it does not interact with existing variables, which means that when macro is evaluated, it should not have any effect on the surrounding code.
258
256
259
257
By default, all macros in Julia are hygienic which means that variables introduced in the macro have automatically generated names, where Julia ensures they will not collide with user's variable. These variables are created by `gensym` function / macro.
260
258
261
-
::: info
262
-
### gensym
263
-
264
-
`gensym([tag])` Generates a symbol which will not conflict with other variable names.
265
-
```julia
266
-
julia> gensym("hello")
267
-
Symbol("##hello#257")
268
-
```
259
+
::: info gensym
260
+
`gensym([tag])` Generates a symbol which will not conflict with other variable names.
261
+
```julia
262
+
julia>gensym("hello")
263
+
Symbol("##hello#257")
264
+
```
265
+
:::
269
266
270
267
Let's demonstrate it on our own version of an macro `@elapsed` which will return the time that was needed to evaluate the block of code.
0 commit comments