Learning Julia (6): expression and macro
Tags: julia, expression, macro
Published: 2020-11-18 17:55:42

Firstly I should cite one paragraph of presentation copied from **Julia** official doc:
> "The strongest legacy of Lisp in the Julia language is its metaprogramming support. Like Lisp, Julia represents its own code as a data structure of the language itself. Since code is represented by objects that can be created and manipulated from within the language, it is possible for a program to transform and generate its own code. This allows sophisticated code generation without extra build steps, and also allows true Lisp-style macros operating at the level of abstract syntax trees(**AST**). In contrast, preprocessor "macro" systems, like that of C and C++, perform textual manipulation and substitution before any actual parsing or interpretation occurs. Because all data types and code in Julia are represented by Julia data structures, powerful reflection capabilities are available to explore the internals of a program and its types just like any other data."


As far as I understand today, code written in Julia would be firstly parsed by compiler as **Expr**, **Symbol** and **QuoteNode** that are structured as **AST**s. Compiler then executes them in the same way how function `eval()` works. In this point of view, **everything in Julia is Expression**.

Below are my learning notes. 

```julia
ex = :(a + b)           # define an expression as :(expression)
println("ex: ", typeof(ex))    # Expr

x = :(ab)               # see what it gives if there is no operator or functions in expression
println("x: ", typeof(x))    # Symbol

println(ex.head, ", ", ex.args)


println("dump(ex):  ") 
dump(ex) ##  equivalent to Meta.@dump :expr


# to define multiple-line expression, one should use: quote ... end
ex1 = quote
a + b
end
println("ex1: ", typeof(ex1))
```

Execution output:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/wio1dmxu5lndzp2qh9ee.PNG)

Expression can be parsed from a string (as our code in src files or typed in terminal are all strings):
```julia
ex = :(a + b)
expr_str = "a + b"
ex2 = Meta.parse(expr_str)


println("ex2: ", typeof(ex2))

println("ex == ex2?? answer is : ", ex == ex2)
println("dump(ex2):  ") 
dump(ex2) 

```
Execution output:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/v20oa4zxn88u7ygg58nk.PNG)


It's also possible to define expression by using `Expr()`
```julia
ex = :(a + b)
ex3 = Expr(:call, :+, :a::Symbol, :b::Symbol)
println("ex == ex3?? answer is : ", ex == ex3)
println("dump(ex3):  ") 
dump(ex3) 

```
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/o38yn0svqlkfaoi5slo5.PNG)

We can use dollar symbol '$' to refer to content of a variable, same as in Linux terminal.
```julia

a = 1;
ex4 = :($a + b)   #  $a yields 1, however if a is not yet defined, there will be an error


println("dump(ex4): ")

dump(ex4) 
```
Execution output:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/58ymzqxtr4wmqzylhoc2.PNG)


With all the stuffs presented above, now it is easy to understand **macro** in Julia. I try to define a macro named sayhello with a "name" variable as input. Let's see how it works.
```julia
macro sayhello(name)
    return :( println("Hello, ", $name) )
end

@sayhello("jemaloQ")

#=
We can view the quoted return expression using the function macroexpand (important note: this is an extremely useful tool for debugging macros):
=#
ex = macroexpand(Main, :(@sayhello("jemaloQ")) )
println("type of ex: ", typeof(ex), ", ex: ", ex)
eval(ex)
```
Execution output:
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/w524ckoolquw4fzqw3ob.PNG)