"using" as a function?

There’s no need for a macro here–if you want a function that has the same effect as using at the top level, then it’s as simple as:

julia> function my_using(x)
         @eval using $(Symbol(x))
       end
my_using (generic function with 1 method)

julia> pkgs = ["LinearAlgebra", "Random"]
2-element Array{String,1}:
 "LinearAlgebra"
 "Random"

julia> my_using.(pkgs)

using has side-effects (bringing names into scope) which would be very surprising for a function. If you run foo() you do not expect the name bar to suddenly appear in global scope.

By the way, if you’re looking to learn more about using macros, I would suggest that you stick to a simple rule of thumb:

Never use Meta.parse, eval, or string concatenation in a macro or in any function used by a macro.

Of course all rules have exceptions, but this is a pretty good guideline to live by when dealing with metaprogramming in Julia. Item by item, the reasons are:

  • You should not need Meta.parse in a macro because a macro already receives a parsed Julia expression. Just use the Expr that the macro received and operate directly on that expression.
  • You should not need eval in a macro because a macro produces code which will eventually be run. If your macro works with the value of some function f(), then it should produce code which calls f() rather than trying to @eval(f()) inside the macro body. The reason is that macro expansion happens when the code is lowered, which is almost never the right time to be calling other functions.
  • You should not need string concatenation because Julia has better tools (like Expr) for representing expressions. Instead of concatenating strings, you can push! into expr.args. This avoids all of the issues like syntax errors or missing escaping of spaces or commas which are all-too-common in lesser macro systems like C/C++.
23 Likes