Using vs import

Why can I load some modules/packages like this

using LinearAlgebra

but if I make my own module named test.jl (located in subdirectory “modules”), I can’t load it like this:

push!(LOAD_PATH,"./modules")
using test

Instead, I have to “include” the module file and then load it with “using”. Why the extra step?

You should not have to use include.

julia> begin
           mkpath("modules")
           write("modules/test.jl",
               "module test end")
           push!(LOAD_PATH, "./modules")
           using test
       end
[ Info: Precompiling test [top-level]

Make sure the file containing the module also has the same name. Also, I would use the absolute path in case you change the working directory.

push!(LOAD_PATH, abspath("./modules"))

Note that Julia conventions are to capitalize module names and use CamelCase for multiple words. There is also a standard library called “Test”.

Manipulating LOAD_PATH like this is going to get messy in the long run.

For multiple packages, I suggest using Pkg.develop.
https://pkgdocs.julialang.org/v1/managing-packages/#developing
Then the paths to different packages can be tracked via your project’s Manifest.toml.

Also see the Code Loading section of the manual.

https://docs.julialang.org/en/v1/manual/code-loading/

The canonical way to organize the code is as follows.

julia> begin
           using Pkg
           Pkg.generate("modules/X")
           Pkg.activate(".")
           Pkg.develop(path=abspath("modules/X"))
           using X
       end
  Generating  project X:
    modules/X/Project.toml
    modules/X/src/X.jl
  Activating project at `~`
   Resolving package versions...
    Updating `~/Project.toml`
  [671976cc] + X v0.1.0 `~/modules/X`
    Updating `~/Manifest.toml`
  [671976cc] + X v0.1.0 `~/modules/X`
Precompiling X
  1 dependency successfully precompiled in 1 seconds

julia> run(`tree modules/X`);
modules/X
├── Project.toml
└── src
    └── X.jl

1 directory, 2 files

julia> run(`cat "modules/X/Project.toml"`);
name = "X"
uuid = "671976cc-d390-4257-a514-cd9c3c96f4a5"
authors = ["root "]
version = "0.1.0"

julia> run(`cat "modules/X/src/X.jl"`);
module X

greet() = print("Hello World!")

end # module X

This seems to be a followup to your previous question about code organization. As you noticed there, often your types (usually structs) have to be declared first. Some examples may be useful.

For small to moderate size packages, I would just use a flat structure without deeply nested modules. An example of this is the types.jl file in HDF5.jl:

On the other extreme is when the types are in a distinct package. These “Core” packages mostly contain just the types:

Meanwhile most of the methods may be defined in the main package:

7 Likes

Thank you for the thoughtful response. That’s helpful. One problem I had was that my module didn’t have the appropriate using statement inside, because I thought that loading them in the parent scope would suffice. So there were types in there that used types defined in “LinearAlgebra” but I was not loading the Linear Algebra library inside my module.

1 Like