Eval from macro

Hi,

Here is some code:

module mypack

export @my_eval

macro my_eval(pkg)
    pp = pkg |> string
    eval(Meta.parse(string("using ", pp)))
end

end

@my_eval CSV

In the code above, I am trying to use a macro to achieve the same as using CSV.
However it does not seem that the package is “loaded”.

How should I correct the code to achieve the same effect as using CSV?

Thank you

Macros and metaprogramming in julia can be a powerful tool, but it is a good idea to only use macros where there’s no other option. I’m not sure what you trying to do, besides loading CSV. So, can’t you just using CSV, instead?

Another thing, using eval inside a macro is not a good practice, because the macro run a parse time. Instead of eval’ing the expression inside the macro, you should return it. So, to correct your macro, you should rewrite as:

macro my_eval(pkg)
    :(using $pkg)
end

But again, what would be the point of such macro if you can just write using pkg?

2 Likes

Even though I agree macros should mostly return expressions that get evaluated, it does work for me.

julia> LinearAlgebra
ERROR: UndefVarError: LinearAlgebra not defined

julia> using .mypack; @my_eval LinearAlgebra

julia> LinearAlgebra # not in call scope
ERROR: UndefVarError: LinearAlgebra not defined

julia> mypack.LinearAlgebra # in definition scope
LinearAlgebra

The eval running in the macro body executes in the global scope of my_eval, thus inside mypack. It won’t change even if you put the eval in a returned expression instead, macro hygiene rules look for functions in the macro definition scope. You can use esc on a function expression to make it in the macro call scope:

julia> module mypack # after restart session

       export @my_eval

       macro my_eval(pkg)
           pp = pkg |> string
           :($(esc(:eval))(Meta.parse(string("using ", $pp))))
       end

       end
Main.mypack

julia> LinearAlgebra
ERROR: UndefVarError: LinearAlgebra not defined

julia> using .mypack; @my_eval LinearAlgebra

julia> LinearAlgebra # now in call scope
LinearAlgebra

julia> mypack.LinearAlgebra # not in definition scope
ERROR: UndefVarError: LinearAlgebra not defined

dylanxyz’s version has a keyword using instead of a function, so it shows up in the call scope.

1 Like

Just a note that if your goal is to load a module programmatically from a name stored in a variable, you can just use @eval directly:

julia> mods = [:LinearAlgebra, "SparseArrays"];

julia> for mod in mods
           @eval using $(Symbol(mod))
       end

julia> LinearAlgebra, SparseArrays
(LinearAlgebra, SparseArrays)
3 Likes

Thank you all for your feedbacks. I am trying to solve the problem I raised in:

With the answer above, I can now create a macro like :
@auto_download using your_package
Which will automatically download, install and load the package without having an annoying prompt asking me whether I want to install the package (in case it is not installed).