Metaprogramming

Macros transform code at parse time, before it runs:

25.1 Code as data: expressions

ex = :(a + b * 2)              # quoted expression
typeof(ex)                     # Expr
ex.head, ex.args               # :call, [:+, :a, :(b * 2)]
dump(ex)                       # tree view
Meta.parse("1 + 2")            # string → Expr
eval(:(1 + 2))                 # 3 (evaluates in global scope of the module)

ex2 = quote
    x = 1
    y = x + 2
end

# Interpolation into expressions
v = 5
:( $v + x )                    # :(5 + x)
args = [:a, :b]
:( f($(args...)) )             # :(f(a, b))

# Building expressions programmatically
Expr(:call, :+, 1, 2) |> eval  # 3

25.2 Macros

Macros transform code at parse time, before it runs:

Hygiene: variables introduced by a macro are renamed (gensym'd) to avoid clashing with user code; use esc() for expressions that should refer to the caller's variables.

Built-in macros you'll use: @time, @show, @assert, @inbounds, @views, @., @printf, @test, @kwdef, @enum, @async, @spawn, @threads, @doc, @info, @generated, @eval, @code_warntype, @inline, @noinline, @nospecialize, @specialize, @static, @ccall, @cfunction, @simd, @fastmath, @goto/@label, @NamedTuple, @lock, @atomic.

macro sayhello(name)
    return :( println("Hello, ", $name) )
end
@sayhello "Julia"

macro mytime(ex)
    quote
        local t0 = time_ns()
        local val = $(esc(ex))
        println("elapsed: ", (time_ns() - t0) / 1e6, " ms")
        val
    end
end
@mytime sum(rand(10^6))

macro unless(cond, body)
    :( if !$(esc(cond)); $(esc(body)); end )
end
@unless 1 > 2 println("ran")

@macroexpand @mytime f(x)      # see the generated code

25.3 Code generation with @eval

for op in (:sin, :cos, :tan)
    @eval my_$(op)(x) = $op(x)
end
my_sin(π/2)

for (name, val) in [(:one_, 1), (:two_, 2)]
    @eval const $name = $val
end

25.4 Enums

@enum Color red green blue
Color(1); instances(Color); Int(blue)

25.5 Useful metaprogramming packages

MacroTools.jl (@capture, postwalk, prettify), ExprTools.jl, MLStyle.jl (pattern matching), Match.jl, Moshi.jl (algebraic data types), Accessors.jl (@set), Parameters.jl.