9 - Metaprogramming
Julia represents its own code as a data structure accessible from 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, that is to create powerful macros (the term "metaprogramming" refers to the possibility to write code that writes code that is then evaluated).
Note the difference from C or C++ macros. There, macros work performing textual manipulation and substitution before any actual parsing or interpretation occurs.
In Julia, macros work when the code has already been parsed and organised in a syntax tree, and hence the semantic is much richer and allows for much more powerful manipulations.
Expressions
There are really many ways to create an expression:
Colon prefix operator
The colon `:` prefix operator refers to an unevaluated expression. Such expression can be saved and then evaluated later using eval(myexpression)
:
Note that $ interpolation (like for strings) is supported:
Quote block
An alternative of the :([...])
operator is to use the quote [...] end
block.
Parse a string
Or also, starting from a string (that is, the original representation of source code for Julia):
Use the Expr constructor with a tree
The expression can be also directly constructed from the tree: expr = Expr(:call, :+, 1, 2)
is equivalent to expr = parse("1+2")
or expr = :(1+2)
.
But what is there inside an expression? Using fieldnames(typeof(expr))
or dump(expr)
we can find that expr
is an Expr
object made of two fields: :head
and :args
:
:head
defines the type of Expression, in this case:call
:args
is an array of elements that can be symbols, literal values or other expressions. In this case they are[:+, 1, 1]
Symbols
The second meaning of the :
operator is to create symbols, and it is equivalent to the Symbol()
function that concatenates its arguments to form a symbol:
a = :foo10
is equal to a=Symbol("foo",10)
A useful example to highlight what a symbol is:
To convert a string to symbol:
Symbol("mystring")
To convert a Symbol to string:
String(mysymbol)
Macros
The possibility to represent code into expressions is at the heart of the usage of macros. Macros in Julia take one or more input expressions and return a modified expressions (at parse time). This contrast with normal functions that, at runtime, take the input values (arguments) and return a computed value.
Macro definition
Macro call
Like for strings, the $
interpolation operator will substitute the variable with its content, in this context the expression. So the "expanded" macro will look in this case as:
Attention that the macro doesn't create a new scope, and variables declared or assigned within the macro may collide with variables in the scope of where the macro is actually called.
You can review the content of this section in this notebook.
While an updated, expanded and revised version of this chapter is available in "Chapter 6 - Metaprogramming and Macros" of Antonello Lobianco (2019), "Julia Quick Syntax Reference", Apress, this tutorial remains in active development.
Last updated