Programmatically/Dynamically import modules in Julia - julia

I was wondering if there was a way to programmatically or dynamically import a set of modules into Julia? For example, if I have a list of files that conform to some naming convention that are present at startup that I can grab using something like:
module_files = filter(r"^mod[0-9][0-9].jl$", readdir())
which might return a list of files ["mod00.jl", "mod02.jl", "mod05.jl"], is there a way to then import each of the modules in those files. This would be equivalent to having:
import mod00
import mod02
import mod05
in the code if I knew that those modules were available when I wrote the code. Or maybe there is some other approach to doing this that is better. Any suggestions would be much appreciated.
Update
I attempted to do this via a macro, but with no luck. For example:
macro import_mod(modn)
quote
import $modn
end
end
function test_mimport()
module_files = filter(r"^mod[0-9][0-9].jl$", readdir())
println(module_files)
for modname in factor_files
modn = modname[1:end-3]
println(modn)
#import_mod modn
end
end
When I run this, I get ERROR: syntax: invalid "import" statement. I tried various escaping strategies but all failed similarly.

NOTE: Please refer to user1712368's (Jameson Nash, Julia dev) answer, this discussion at the julia-users mailing list and this entry of the Julia manual, to know about why this is not the correct answer.
This is how a multiple import expression looks like, using Julia version 0.3.4:
julia> parse("import Foo, Bar")
:($(Expr(:toplevel, :($(Expr(:import, :Foo))), :($(Expr(:import, :Bar))))))
julia> dump(ans)
Expr
head: Symbol toplevel
args: Array(Any,(2,))
1: Expr
head: Symbol import
args: Array(Any,(1,))
1: Symbol Foo
typ: Any
2: Expr
head: Symbol import
args: Array(Any,(1,))
1: Symbol Bar
typ: Any
typ: Any
Here is a macro which does this programmatically, it takes a modules argument that could be a :call or :vcat Expr or a Symbol, which has to evaluate to a Vector{Symbol}:
julia> macro dynamic_import(modules)
(modules = eval(modules))::Vector{Symbol}
ex = Expr(:toplevel)
for m in modules
push!(ex.args, Expr(:import, m))
end
return ex
end
You could also generalize this line:
module_files = filter(r"^mod[0-9][0-9].jl$", readdir())
By abstracting it into a function that takes a Regex and a String directory path as arguments and returns a Vector{Symbol}:
julia> function needed_modules(rx::Regex, dir::String=".")
module_files = filter(rx, readdir(dir))
module_syms = map(m -> symbol(split(m, '.')[1]), module_files)
end
needed_modules (generic function with 2 methods)
So you may use it like this:
julia> #dynamic_import [:Mod01, :Mod02] # :vcat expression
julia> rx = r"^Mod[0-9][0-9].jl$";
julia> #dynamic_import needed_modules(rx) # :call expression
julia> modules = needed_modules(rx)
2-element Array{Symbol,1}:
:Mod01
:Mod02
julia> #dynamic_import modules # Symbol
Finally you could wrap it all into a module so you may use using DynamicImport:
Note: currently I get this when trying to run the same examples from a module:
julia> using DynamicImport
julia> #dynamic_import [:mod01, :mod02]
julia> rx = r"^mod[0-9][0-9].jl$";
julia> #dynamic_import needed_modules(rx)
ERROR: rx not defined
julia> modules = needed_modules(rx)
2-element Array{Symbol,1}:
:mod01
:mod02
julia> #dynamic_import modules
ERROR: modules not defined
But it works fine If I define the objects inside the REPL, I guess this is an issue involving hygiene, which is something I'm not experienced with, so I'll ask at the julia-users mailing list.

The function version of import X is require("X"), so you should be able to do:
function test_mimport()
module_files = filter(r"^mod[0-9][0-9].jl$", readdir())
println(module_files)
for modname in factor_files
modn = modname[1:end-3]
println(modn)
require(modn)
end
end
Then, assuming each of these defined a module of the same name, you can collect them into an array:
modules = Module[]
...
if isdefined(Main, symbol(modn))
push!(modules, getfield(Main, symbol(modn))
else
warn("importing $modn did not defined a module")
end
...
return modules

I'm not thrilled with this solution, but it seems to work. Basically I dynamically create a temporary file contains the import statements that I then include it. So:
function test_mimport()
module_files = filter(r"^mod[0-9][0-9].jl$", readdir())
path, f = mktemp()
for modname in module_files
modn = splitext(modname)[1]
write(f, "import $modn\n")
end
close(f)
include(path)
rm(path)
end
I would be curious to know if there is a more idiomatic strategy since I'm new to Julia and just wadding through its capabilities.
Edit
The strategy of setting up a temporary file is likely unnecessary since Julia also provides an include_string method.
On the Julia-user list, #Isaiah also suggests using eval(Expr(:import, symbol(modn)))

Related

Get struct property from inside struct function errors out

Recently started to try and learn Julia through examples. I am basically trying to figure out how to access a struct property from within a function inside the struct itself. E.g:
struct Test
a::Int
foo::Function
function Test()
return new(777, xfoo)
end
function xfoo()
println(a)
end
end
t = Test()
t.foo()
I get:
ERROR: LoadError: UndefVarError: a not defined
Stacktrace:
[1] (::var"#xfoo#1")()
# Main /tmp/j.jl:10
[2] top-level scope
# /tmp/j.jl:15
in expression starting at /tmp/j.jl:15
Am I using Julia wrong or am I missing something?
Julia is not object oriented language so object oriented patterns are usually not a good idea.
Hence xfoo should be outside of Test:
function xfoo(t::Test)
println(t.a)
end
There are packages that try to emulate OOP with Julia (however this is not a Julian pattern): https://github.com/Suzhou-Tongyuan/ObjectOriented.jl
You can also easily find quite a lot of discussion behind the design decision no to make Julia OOP. Start with: https://discourse.julialang.org/t/why-there-is-no-oop-object-oriented-programming-in-julia/86723
Workaround
Just out of curiosity one can find some workaround to attach a function to a struct (not a recommended design pattern!). For an example:
mutable struct MyTest
a::Int
foo::Function
function MyTest()
s = Ref{MyTest}()
s[] = new(777, () -> println(s[].a))
s[]
end
end
And some sample usage:
julia> t = MyTest();
julia> t.foo()
777
julia> t.a = 900;
julia> t.foo()
900

JULIA include statement error: applicable method may be too new

I want to import a function present in a julia file somewhere during runtime
just like in python we have importlib.import_module to import module is there something present in julia
I'm new to julia and I'm not sure how to do that.
I have to import a function main from another julia file and want to run it but I have to also check a condition before that if the condition is true then I want to import the function.
EDIT
I have a file
main.jl
function myMain()
s1 = "Hello"
s2 = "World!"
include("functions/hello.jl")
say(s1, s2)
end
myMain()
hello.jl
function say(s1, s2)
print(s1, s2)
end
Error
ERROR: LoadError: MethodError: no method matching say(::String, ::String)
The applicable method may be too new: running in world age 32378, while current world is 32379.
Closest candidates are:
say(::Any, ::Any) at ~/Desktop/julia_including/functions/hello.jl:1 (method too new to be called from this world context.)
Stacktrace:
[1] myMain()
# Main ~/Desktop/julia_including/main.jl:5
[2] top-level scope
# ~/Desktop/julia_including/main.jl:8
in expression starting at /home/shivansh/Desktop/julia_including/main.jl:8
It works fine when I don't use include inside the myMain() function in main.jl
Julia contrary to Python is a compiled language.
Hence for maximum performance all functions should be known at the compilation time so efficient assembly code can be generated.
Hence generally you want to avoid syntax like the one you are proposing and have the inlude outside of the function.
However, if you really know what you are doing, why you are doing this and need such functionality then comes the world of Julia metaprogramming.
The code is the following:
function myMain()
s1 = "Hello"
s2 = "World!"
include("say.jl")
Base.invokelatest(say, s1, s2)
end
Now you can do:
julia> myMain()
HelloWorld!
Honestly, it just sounds like you wanna do this
if some_condition
include("/path/to/some/file/that/you/need.jl")
else
include("/path/to/some/OTHER/file/that/you/need.jl")
end
EDIT
I think though, after seeing the error that you added, what you wanna do is define your function multiple times instead, and with different argument types:
function say(x::Any, y::Any)
println("Default")
end
function say(x::String, y::Any)
println("String in arg1: $x")
end
function say(x::Any, y::String)
println("String in arg2: $y")
end
function say(x::String, y::String)
println("String in args 1 and 2: $x $y")
end
That way using say differs based on the datatype of the arguments:
julia> say("Foo", "Bar")
String in args 1 and 2: Foo Bar
julia> say("Foo", 2)
String in arg1: Foo
julia> say(0, "Bar")
String in arg2: Bar
and if you have two different functions that act on 2 strings you can do this instead:
struct MySayFunctionType{Symbol}
end
function say(x::String, y::String, ::MySayFunctionType{:Type1})
println("Type1 function is being called: $x $y")
end
function say(x::String, y::String, ::MySayFunctionType{:Type2})
println("Type2 function is being called: $x $y")
end
and then use these like this
if condition
say("Foo", "Bar", MySayFunctionType{Symbol("Type1")}() )
else
say("Foo", "Bar", MySayFunctionType{Symbol("Type2")}() )
end
and if you want a default version of this function to be called -- say Type2 -- you can just add a default value for the third argument like this:
function say(x::String, y::String, z::MySayFunctionType{:Type2}=MySayFunctionType{Symbol("Type2")}())
println("Type2 function is being called: $x $y")
end
The reason this works is because "MySayFunctionType{:Type1}" is considered to be a different type than "MySayFunctionType{:Type2}" so multiple dispatch would consider that as well.

Julia Macro to Save the Overt form of a Function, and Define it

Some of the parameters to a simulation I am writing are functions. When the output is generated, I want to put the definition of these functional parameters in the output. I have in mind a macro that somehow saves the definition as a string, and then defines it. For example, here's what I do now:
borda_score_fn(p) = exp(1/p)
global g_borda_score_fn_string = "exp(1/p)"
And then I write g_borda_score_fn_string to my output. But this is really ugly!
What I would like to do is something like this:
#paramfn borda_score_fn(p) = exp(1/p)
And later be able to both call borda_score_fn(p), and have the form (i.e., "exp(1/p)") available for writing to my output log. (The string form might get stashed in a global dict, actually, they both could.)
I have tried many version of this, but can't get the right set of parses and calls to get it to work. Any help would be appreciated.
This may be a bit different than what you have in mind, but one perhaps "Julian" approach might be to have the function itself return the form string via multiple dispatch, rather than defining a whole new global variable just for that. For example, say we have a type
struct Form end
that we can use for dispatch, then we can write
borda_score_fn(p) = exp(1/p)
borda_score_fn(::Form) = "exp(1/p)"
which can then be retrieved just by calling the function with our type
julia> borda_score_fn(2)
1.6487212707001282
julia> borda_score_fn(Form())
"exp(1/p)"
That might actually be not bad on its own. But, if you want a macro to do both parts at once, then something along the lines of
macro paramfn(e)
name = esc(e.args[1].args[1])
str = string(e.args[2].args[2])
f = esc(e)
quote
$name(::Form) = $str
$f
end
end
would let you write
julia> #paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 2 methods)
julia> borda_score_fn(1)
2.718281828459045
julia> borda_score_fn(Form())
"exp(1 / p)"
For completeness, here's how you can do it in a way more similar to your original approach, but more idiomatically than with a global variable:
julia> module FormOf
export formof, #paramfn
function formof end
macro paramfn(expr)
name = esc(expr.args[1].args[1])
form_str = string(expr.args[2].args[2])
quote
$(esc(expr))
$FormOf.formof(::typeof($name)) = $form_str
$name
end
end
end
Main.FormOf
julia> FormOf.#paramfn borda_score_fn(p) = exp(1/p)
borda_score_fn (generic function with 1 method)
julia> FormOf.formof(borda_score_fn)
"exp(1 / p)"
However, since it defines a new method of FormOf.formof, this only works in global scope:
julia> function bla()
FormOf.#paramfn fn(p) = exp(1/p)
fn(10) + 1
end
ERROR: syntax: Global method definition around REPL[45]:10 needs to be placed at the top level, or use "eval".
Stacktrace:
[1] top-level scope
# REPL[50]:1
#cbk's solution does not have this limitation.

Does Julia have function decorators?

In some languages (like Python) there are function decorators which appear like macros and sit above a function definition. Decorators provide some additional functionality to the function itself.
Does Julia support this idea of function decorators in any way? Would it be possible to use macros to accomplish the same goal?
Here is one way to achieve this with a macro using ExprTools.jl:
import ExprTools
macro dec(decorator, inner_def)
inner_def = ExprTools.splitdef(inner_def)
outer_def = copy(inner_def)
fname = get(inner_def, :name, nothing)
if fname !== nothing
#assert fname isa Symbol
inner_def[:name] = Symbol(fname, :_inner)
end
outer_def[:body] = Expr(:call,
:($decorator($(ExprTools.combinedef(inner_def)))),
get(outer_def, :args, [])...,
get(outer_def, :kwargs, [])...,
)
return esc(ExprTools.combinedef(outer_def))
end
julia> _time(f) = (x...) -> (t0=time(); val=f(x...); println("took ", time()-t0, " s"); val)
_time (generic function with 1 method)
julia> #dec _time function foo(x, y)
return x + y
end
foo (generic function with 1 method)
julia> foo(1, 2)
took 9.5367431640625e-7 s
3
However, at least in my experience, this comes up surprisingly rarely in idiomatic Julia programs. I would first think about whether your specific problem might be better solved just by multiple dispatch with plain higher-order functions and traits, which will usually lend itself to better composability.
To expand the comment of #SilvioMayolo, Julia has pretty elegant macro capability, that is more general than the Python decorator.
Indeed Julia's macro take as input any expression, and the macro can hook over the Abstract Syntax Tree to return a modified expression.
The keyword you want to look for is "metaprogramming".
Here you can find the official documentation and here a convenient tutorial.

Get a warning when I overwrite a function in Julia?

Julia by default imports many names into scope. Is there a way to get a warning when I inadvertently overwrite one of them?
In the context of modules and Base functions, Julia already does warn you if you overwrite a name. See the below examples that work on v0.4.5:
MODULES:
In modA.jl:
module modA
export test
function test()
println("modA")
end
end
In modB.jl:
module modB
export test
function test()
println("modB")
end
end
In REPL:
julia> using modA
julia> using modB
WARNING: Using modB.test in module Main conflicts with an existing identifier
julia> test()
"modA"
BASE FUNCTIONS:
In REPL:
julia> function +(x::Float64, y::Float64)
println("my addition")
end
julia> WARNING: module Main should explicitly import + from Base
WARNING: Method definition +(Float64, Float64) in module Base at float.jl:208
overwritten in module Main at none:2.
As far as I am aware, this does not work with user defined functions; see below:
julia> function test(x::Float64, y::Float64)
println("First Definition")
end
julia> test(1.0, 2.0)
First Definition
julia> function test(x::Float64, y::Float64)
println("Second Definition")
end
julia> test(1.0, 2.0)
Second Definition
Did you have a different context in mind for imported names?

Resources