Pros & Cons of using modules vs plain include?

I would like to understand the two ways of structuring and running scripts:

  1. put the scripts in modules, and use ‘using MyModule’ to run them;
  2. not put the scripts in any module, and use ‘include(“myfile.jl”)’ to run them.

So scripts in modules get precompiled, and scripts not in modules don’t. Does that make scripts in modules start up faster?

Doing ‘using MyModule’ multiple times does not seem to matter, but doing ‘include(“myfile.jl”)’ multiple times does seem to get some warnings. And, each ‘include’ does seem to increase julia’s memory usage, doesn’t it?

After you edit parts of a module in some way, you can’t simply do ‘using MyModule’, you have to restart julia, which slows down the next run of the script because of loading/compiling. In certain cases which I can’t specify, even the magic of Revise does not help.

And if you don’t use modules, you can keep doing (except when you modified the fields of certain composite types) ‘include(“myfile.jl”)’ until julia’s memory usage gets very high, at which time you need to restart julia.

And So on,…
So, what really are the pros and cons of the two ways?

Use projects, which are modules + Project.toml + Manifest.toml. No need to dev them, just activate when you are working on a problem (either with pkg"activate /path/to/project", or julia --project=path).

This will give you

  1. the ability to use Revise.jl (a big win, hard to overemphasize its importance),
  2. a consistent environment that is always reproducible and only changes when you want it to (put Manifest.toml in version control),
  3. precompilation of the project and all of its dependencies (faster startup).

Don’t use single-file scripts for anything else than short throwaway code. Generating and using projects is so simple (pkg> generate path) that it is a no-brainer to do it.

8 Likes

On a related note that would prevent this solution. What if a module is used as a namespace, rather than a module per se, i.e. isn’t standalone and can’t be made into a package?

Is the answer just to not use modules incorrectly?

I am not sure I understand. Can you clarify what you mean by this, perhaps with an example?

Lets say we have a genuine package Foo.jl with code inside module Foo, and it has a couple of files Bar.jl an Baz.jl. Bar.jl and Baz.jl are included into Foo. The problem arises because there in their respective files there is a module Bar and a module Baz, which only serve to make it abundantly clear that that Baz stuff is well separated from Bar stuff, and also for making functions “private” etc. The code would work just as well without modules Bar and Baz because they’re really only namespaces for keeping code tidy. As it stands it’s not possible to make a Bar or a Baz package, and as such do they represent misuses of modules?

How do you load Bar and Baz?

You may be diving deeper than the pool. It should not matter whether a module is intended to provide some computational facility or to be a gathering umbrella.
module M; export solve; function solve(x) ... end # module
module M; export insideofm; const insideofm = 12; .. end # module
To use the former, using M; ... solve(x).
To use the latter, import M; M.insideofm
or, using M:insideofm; insideofm

3 Likes

Very interesting question and answer. I have a difficulty finding these tricks in the documentations. Is there a beginner tutorial/documentation on how to setup projects?

1 Like

Pkg3 is documented, but this is WIP. I would expect that a lot of the tooling and the tutorials will catch up within a few months.

1 Like

After you edit parts of a module in some way, you can’t simply do ‘using MyModule’, you have to restart julia, which slows down the next run of the script because of loading/compiling. In certain cases which I can’t specify, even the magic of Revise does not help.

And if you don’t use modules, you can keep doing (except when you modified the fields of certain composite types) ‘include(“myfile.jl”)’ until julia’s memory usage gets very high, at which time you need to restart julia.

AFAIK the only remaining major issue is type redefinitions, everything else is covered. If not, I opening an issue for Revise may get you closer a solution.

With include and import.

When using import, how is it found? Do you put its directory in the load path?

I include by include(src/Bar.jl) and then import works. I’m only using 0.6 though if that makes a difference

I would not call it a misuse of modules then; if this is advantageous for your project setup then go for it. That said, I would just use Requires.jl for conditional functionality.