I am messing around with Pluto and decided I would like a shorter way of importing libraries. This question is sort of two fold. How can I dynamically import Modules using a macro or something else? And why is using a keyword a not a function? I suppose this could go into the Julia internals subject, but this just struck me as strange.
What I would like to do.
using Pkg
pkgs = ["HTTP","DataFrames","CSV"]
Pkg.add(pkgs)
using.(pkgs)
I understand this is probably something that I should just overlook, but I found it a good chance to try and learn macros, but I only got close to what i wanted.
function toLine(arr)
out = ""
for i in 1:1:length(arr)
if i == length(arr)
out *= arr[i]
else
out *= arr[i] * ", "
end
end
return out
end
macro useit(libs)
return Meta.parse("using " * toLine(libs))
end
@useit(pkgs)
LoadError: MethodError: no method matching length(::Symbol)
Closest candidates are:
length(!Matched::Core.MethodTable) at reflection.jl:957
length(!Matched::CompositeException) at task.jl:41
length(!Matched::Base.Iterators.Flatten{Tuple{}}) at iterators.jl:1061
Iām sure I could do it with a macro, I was just curious if anyone knew if there is a function for this or a way of calling āusingā that makes this work. I just noted that it was strange I could add Packages using a variable, but not easily āusingā the Modules.
So much stuff is a function I am confused as to why using would be something special.
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++.
That makes utter sense and is highly appreciated. I suffer from not understanding meta programming yet and am an amateur programmer. I started with cpp and have just picked up the hobby of checking out languages. Julia is really interesting, like a glue language. Anyways, thanks for the well reasoned and thought out answer!
Just to clarify the bit about avoiding string concatenation: Does this apply only to modifying julia code strings (which would then be parsed), or does this apply to string concatenation in general?
In principle also applies if you are parsing the result of string manipulation into some structure, even if the parsing result isnāt julia code. Case in point, donāt use string manipulation to construct a JSON string just to parse it immediately (if you are doing that to pass it to javascript thatās probably the best you can do so thatās fine). Obviously string manipulation is still useful and you should use it when you need a string.
Yeah, @Mason is rightāI mean avoid concatenating strings to build Julia code. Itās still fine to concatenate "hello" and "world" if your problem requires it
I am a very slow learner about macros. I had a macro that added a seldom-used directory to my LOAD_PATH with eval. I was only calling this thing from the REPL and notebooks. I thought it had to be a macro, but making it a function worked as well.
@Oscar_Smith says, "I feel like someone has to give this answer about one a week.āā, but some of us need to see it several times before it sinks in.