DEV Community

Cover image for Learning Julia (6): expression and macro
Z. QIU
Z. QIU

Posted on

Learning Julia (6): expression and macro

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 ASTs. 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.

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))
Enter fullscreen mode Exit fullscreen mode

Execution output:

Alt Text

Expression can be parsed from a string (as our code in src files or typed in terminal are all strings):

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) 

Enter fullscreen mode Exit fullscreen mode

Execution output:
Alt Text

It's also possible to define expression by using Expr()

ex = :(a + b)
ex3 = Expr(:call, :+, :a::Symbol, :b::Symbol)
println("ex == ex3?? answer is : ", ex == ex3)
println("dump(ex3):  ") 
dump(ex3) 

Enter fullscreen mode Exit fullscreen mode

Alt Text

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


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) 
Enter fullscreen mode Exit fullscreen mode

Execution output:
Alt Text

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.

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)
Enter fullscreen mode Exit fullscreen mode

Execution output:
Alt Text

Top comments (0)