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 # 325.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 code25.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
end25.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.