Julia: no way to include only once.

Created on 5 Jan 2019  Â·  11Comments  Â·  Source: JuliaLang/julia

Hello!

I'm trying to make a big modular project with a lot of folded files/modules.
And once upon a time, today, I got a touble.
After a long research, I found that include() literally inserts contents of a file into parent's sourcecode, that is extremely uncommon practice in other languages (#ifndef is the worst case), makes the compiler much slower due to more code to be processed again, shoots feet due to absence of consisstency and different pointers etc.
After several hours of suffering I still cannot do it without crashing my structure to a longest-ever include() list into my main.jl with unpredicted variable collision behavior.

Why don't just make some function like include_once ?

const included_sources::Array{AbstractString} = []
function include_once(path::AbstractString)
    if path ∉ included_sources
        push!(included_sources, path)
        include(path)
    end
end

Most helpful comment

The following is for beginners like me who stumbled across this post trying to figure out the basics of file organization, (before getting to slightly more advanced topics like module and package creation) and wondering why there isn't an easy way to do something like C++'s header guards.

The key is to include everything in the "lowest-level" file once, and nowhere else. An example: Assume you have main.jl, A.jl, B.jl, and C.jl. Assume:
-main.jl needs A.jl and B.jl and C.jl
-A.jl needs B.jl and C.jl
-B.jl needs C.jl.

The key here is to NOT use include statements in A.jl and B.jl. Instead, you find the “lowest-level” file, in this case main.jl, and put all the include statements there, in the correct order. In main.jl:

include("C.jl")
include("B.jl")
include("A.jl")

And then NO include statements ANYWHERE else. For more on this, see https://www.reddit.com/r/Julia/comments/hwxpgm/beginner_question_files_modules_and_include_vs/, particularly user a5sk6n’s outstanding answer in the comments.

All 11 comments

To be clear, the way to do this is to organize code into packages.

@JeffBezanson

To be clear, the way to do this is to organize code into packages.

How? I have ~20 modules at the early stage app with ~100 lines each. Modules often have very similar names (something like Wing (in a folder and module named Plane and in a folder named Bird)).
If make a package for each sharable module, it would already become unmaintable.

@yuyichao

Dup of #29966

Not completely my case. Please, see above. Code is pretty tied and variables have to be robustly shadowed. Isn't there any way to avoid global module/package name pollution?

PS: trying to use julia in combined way: complete server app + statistical analysis in one place.

PPS: is this https://github.com/JuliaComputing/JuliaDB.jl/blob/master/src/JuliaDB.jl a true julia way?

How? I have ~20 modules at the early stage app with ~100 lines each.

See mention of #4600 in the issue I linked.

Not completely my case.

It basically is, please read through both issue linked.

Please, see above. Code is pretty tied and variables have to be robustly shadowed. Isn't there any way to avoid global module/package name pollution?

There is no code (unless you mean your include_once, which isn't your actual code). It's unclear what you mean by code being tied and variables being shadowed, you are just not supposed to include the same file more than once unless you specifically want that. That's not what include is for. There's no global module / package name pollution.

As you can see in #4600, no one is saying that a python-like on-demand loading shouldn't be useful on sub-module/package level but neither this or #29966 are the correct way to fix it. The issue you mentioned in the top post are either not a problem or because you want to use include for what it is not for. #4600 is useful, but it should be a feature of importing, not of include.

@yuyichao For instance, I've got a database driver module called DBInterface. I use it in four different storage modules.

/DBInterface/DBInterface.jl
/Storages/Storage1.jl
/Storages/Storage2.jl
...

How shall I import DBInterface in those modules to keep it simple and reliable? Thanx!

PS again: I've carefully read those issues and docs too and still cannot figure out a bit effective way of doing this.

How shall I import DBInterface in those modules to keep it simple and reliable? Thanx!

Just import it? (And after including all the files). include is a way to organize files and import is the way to, well, import dependencies. Do note that, as I mentioned many times in the other issue, your version of the code does not actually work if you want to have any nested submodules and in particular does not work if you want to import DBInterface from a Storage* module.

Please ask further questions about the correct way to use include and import, or anything you still have about why this is not the right solution to any of the related issues on discourse.julialang.org and I'll be happy to go into much more detail there. The answer above is already an exception since it's very slightly related to the reason this issue is closed.

The following is for beginners like me who stumbled across this post trying to figure out the basics of file organization, (before getting to slightly more advanced topics like module and package creation) and wondering why there isn't an easy way to do something like C++'s header guards.

The key is to include everything in the "lowest-level" file once, and nowhere else. An example: Assume you have main.jl, A.jl, B.jl, and C.jl. Assume:
-main.jl needs A.jl and B.jl and C.jl
-A.jl needs B.jl and C.jl
-B.jl needs C.jl.

The key here is to NOT use include statements in A.jl and B.jl. Instead, you find the “lowest-level” file, in this case main.jl, and put all the include statements there, in the correct order. In main.jl:

include("C.jl")
include("B.jl")
include("A.jl")

And then NO include statements ANYWHERE else. For more on this, see https://www.reddit.com/r/Julia/comments/hwxpgm/beginner_question_files_modules_and_include_vs/, particularly user a5sk6n’s outstanding answer in the comments.

For the benefit of beginners who find this, package creation is not hard:

julia> using PkgTemplates
julia> t = Template()
julia> t("MyPkg")
(@v1.5) pkg> dev MyPkg    # command is from pkg mode (hit ']' to enter, Ctrl-C to exit)

Now any code that you put in MyPkg will only ever be loaded once per session.

@timholy any way to make'em local to a project or a directory inside it to avoid name pollution ?

Just activate whichever environment you want before the dev.

Was this page helpful?
0 / 5 - 0 ratings