Is "include" safe to use inside a function?


#1

Is the following safe to do?

#testinclude.jl
a = 2
julia> function f()
           include("testinclude.jl")
           return a
       end
f (generic function with 1 method)

julia> f()
2

#2

It’s safe, but there’s probably a better way to do what you’re trying to do. What is your use case exactly?


#3

Awesome. My use case is running experiments programmatically given a function that lives in a file, where the file name and the function name are retrieved from a database. I think I can do without the above feature actually. Thanks though!


#4

But note that include includes the code in the global scope, i.e. it’s not just copy-paste, which is what you probably had in mind. Consider including a file tmp.jl which just contains a=1:

julia> function f()
       include("tmp.jl")
       1
       end                                                                                                                                                               
f (generic function with 1 method)                                                                                                                                       

julia> a                                                                                                                                                                 
ERROR: UndefVarError: a not defined                                                                                                                                      

julia> f()                                                                                                                                                               
1                                                                                                                                                                        

julia> a                                                                                                                                                                 
1                                                                                                                                                                        

So, a is a global variable after running the function. So include is like running eval in a local scope.

If you want to copy-paste different bits of code into a function, you could consider using a macro:


julia> macro ins()
       esc(:(a = 1)) # esc is necessary
       end
@ins (macro with 1 method)

julia> function f(x)
       @ins
       x + a
       end
f (generic function with 1 method)

julia> f(4)
5

julia> macro ins()
       esc(:(a = -10))
       end
@ins (macro with 1 method)

julia> f(4) # need to re-define the function to get the update
5

julia> function f(x)
       @ins
       x + a
       end
f (generic function with 1 method)

julia> f(4)
-6

However, in most cases I don’t think this is particularly nice style.


#5

I see, thanks for the detailed response. I opted for a macro to be presumably called by the user in global scope. Since there will be using and include statements needed before running the functions, and I might need parse and eval, I think my application really belongs to global scope. Also type instability is not really an issue in my use case.


#6

This discussion makes me wonder why “include” is not already a macro, rather than a function. After all, even in C is a macro.


#7

Because it can be a function—principle of least privilege.


#8

This discussion should show exactly why it shouldn’t be a macro. By being a function, it should be very clear that it cannot do anything that’s not in global scope. It also makes it clear that the include happens at runtime and you can create a wrapper function on it without ANY semantics significance. Note that these are properties on ALL functions.

If you don’t want any of these properties, you may need a macro. Making it happen at compile time (whether on global or local scope) is trivial to do and I’m pretty sure someone had a macro / package for it. It’s purely for source code organization but since it isn’t particularly easy for the reader and isn’t needed in Base, it is not included in Base.

If you want it to do something to the local scope and at runtime, that’s impossible. The usecase is basically calling user supplied code and it should use callback/function overload instead.


#9

Does Julia allow creating a new environment? So then the experiment can be run in that environment and destroyed afterwards. But if that’s the case then it may be good to just spawn a new process…


#10

You can create new modules. They will almost never be freed though


#11

Does this mean that objects whose only bindings are at global scope in a given module are not garbage-collected even if the module itself is “replaced” by a new module with the same name?


#12

Not always but very likely.