Why is "macro" different from "function" if everything is an expression?

I’m learning the basics of macros. Coming from another LISP-inspired language (Wolfram Mathematica), I’m wondering why there is a distinction between macros and functions. Isn’t a macro just a function whose argument is an expression? Is it correct to say that the “macro” keyword is just syntax sugar which allows you to write
@some_marco expression
instead of
some_function(:(expression)) ?

1 Like

Welcome to Julia-discourse!
No, the difference is when the function/macro is evaluated. The latter is done at parse-time, i.e. before the code is run. Watch https://youtu.be/RYZkHudRTvI

5 Likes

You can actually call it as a function. Try var"@some_macro"(LineNumberNode(@__LINE__, @__FILE__), @__MODULE__, :(expression)) instead, e.g.

julia> var"@show"(LineNumberNode(@__LINE__, @__FILE__), @__MODULE__, :(1 + 2))
quote
    println("1 + 2 = ", repr(begin
                #= show.jl:641 =#
                value = $(Expr(:escape, :(1 + 2)))
            end))
    value
end

The var"..." around the macro name is just so that the @ in front of the name gets interpreted as just a name, not as an actual macro call. The LineNumberNode and module tell the macro where it is expanded in, which can be useful for debugging and a lot of other things.

4 Likes

Thanks for the explanation. If you don’t care about performance penalties, is it OK to use a function to manipulate the syntax tree of an expression and then hit the modified expression with “eval”? For example, here’s a function which changes any integer assignment like “a=100” into a float assignment like “a=100.0”.

function change(expr::Expr)
    if expr.head == :(=)
        expr.args[2] = float(expr.args[2])
        eval(expr)
        return 0
    else
        return -1
    end
end

Then I can call the function with
change(:(a=100))

In the end, the value of “a” will be the floating-point number 100.0.

No it’s not. They have completely different scope. There’s no way to emulate macros with eval. eval only runs in global scope and have no access to any local variables that’s accessible by code returned from macros.

9 Likes