Search code examples
metaprogrammingjuliaquoting

Disambiguate the various quoting mechanisms in Julia metaprogramming


Could someone lay out clearly the various quoting mechanisms available for metaprogramming in Julia, and illustrate each one with a minimal example?

So that it is clear which to use in which situation...

As far as I can see there is:

  • :(someExpr; maybeMore)
  • quote; ...expressions... end; which is almost the same as the above only with line numbers for debug purposes
  • Expr(:quote, x) which (according to @totalverb) is equivalent to Meta.quot(x)
  • QuoteNode which (according to Jeff Bezanson) "is only used in the internal AST representation, not by macros. It's safe to ignore unless you work on later stages of the compiler."

Is this list comprehensive? Am I missing out any alternate representations?


Solution

  • :... is the default quoting mechanism. It parses an expression and returns the AST.

    :x == Symbol("x")
    
    :(x + y) == Expr(:call, :+, :x, :y)
    
    :(x; y; z) == Expr(:block, :x, :y, :z)
    

    eval(:<expr>) should return the same as just <expr> (assuming <expr> is a valid expression in the current global space)

    eval(:(1 + 2)) == 1 + 2
    
    eval(:(let x=1; x + 1 end)) == let x=1; x + 1 end
    

    quote ... end is the same as :(begin ... end)


    Expr(:quote, x) is used to represent quotes within quotes.

    Expr(:quote, :(x + y)) == :(:(x + y))
    
    Expr(:quote, Expr(:$, :x)) == :(:($x))
    

    QuoteNode(x) is similar to Expr(:quote, x) but it prevents interpolation.

    eval(Expr(:quote, Expr(:$, 1))) == 1
    
    eval(QuoteNode(Expr(:$, 1))) == Expr(:$, 1)
    

    Here's a macro using all of them:

    macro quoted(expression)
        quote
            println("received expression: :(", $(QuoteNode(expression)), ")")
            $(Expr(:quote, expression))
        end
    end
    

    Usage:

    julia> x = 1
    1
    
    julia> @quoted $x + 1
    received expression: :($(Expr(:$, :x)) + 1)
    :(1 + 1)
    
    julia> @quoted :(x + 1)
    received expression: :($(Expr(:quote, :(x + 1))))
    :($(Expr(:quote, :(x + 1))))
    

    edit: Meta.quot(x) is indeed the same as Expr(:quote, x). It's undocumented and isn't mentioned anywhere, but it was a conscious addition to the functions exported by Base (https://github.com/JuliaLang/julia/pull/1755) and I haven't seen any plans to deprecate it, so you can use it.