Avoid repeating the same `using` line for enclosed modules

I want to write modules inside of a module. Is there a way to avoid repeating the same using line of code?

module Module0

using Pkg1, Pkg2, Pkg3

module Module01
using Pkg1, Pkg2, Pkg3
some_code_1
end

module Module02
using Pkg1, Pkg2, Pkg3
some_code_2
end

end

.

1 Like

One option would be to use the @def macro that @ChrisRackauckas has mentioned here previously:

 macro def(name, definition)
    return quote
        macro $(esc(name))()
            esc($(Expr(:quote, definition)))
        end
    end
end

which you could use like this:

@def use_packages begin
    using Pkg1, Pkg2, Pkg3
end

module Module01
@use_packages
end
2 Likes

Consider instead, not using submodules.
I wrote a SO answer about this a while ago.

I find the using nested modules rarely worksout for the best.
Especially if you are using many of them.

Not a hard-rule.
But it is good to stop and check that the submodules are doing anything useful for you.

4 Likes

I completely agree with this. A simplification:

  1. Function names should be short and simple, and should use dispatch in order to distinguish between methods. So they should all be named the same, but dispatch will take care of any possible issues.

  2. Types should have unique names. Otherwise it would be confusing, right?

  3. Most of Julia is encoded in functions and types.

This is the same reason why import * from ... is not good in Python, but exporting stuff from Julia modules is okay. “Clashing” isn’t really a problem if you are doing dispatch on unique (and aptly named) types. This gives a lot less of an incentive to submodulize compared to other languages.

But if you do need to, then yes, the @def macro will do compile time copy/pasting for you.

2 Likes

I love submodules. My packages typically use them generously. But if all the submodules are using the same dependencies, that is a strong indicator that perhaps they are more coupled than is typical, and might be better off in the same module.

2 Likes

Thank everyone for the helpful comments!

I’m having a discussion with our software lead about how we implement an API in Julia, where users call a function analyze. She wants different analyze functions that perform different types of analyses, and to avoid namespace issues, she wants me to use modules, i.e. using different module names for different types of analyses, but every such module will have one function called analyze. Among the types of analyses we already set up, many of them will have essentially the same dependencies (using the same set of packages), meaning they are too coupled to be in separate modules? The issue of using multiple dispatch is that she wants exactly the same function signature for each analyze function. So I’m not sure how to propose better alternatives.

Thanks for any potential help!

1 Like

One alternative to submodules in your case would be to use an additional argument to distinguish the type of analysis to do. For example:

abstract AnalysisType

immutable Linear <: AnalysisType
end

immutable Quadratic <: AnalysisType
end

analyze(::Type{Linear}, x) = println("Linear analysis of $x")
analyze(::Type{Quadratic}, x) = println("Quadratic analysis of $x")

which you could run like this:

analyze(Linear, [1,2,3])

With a design like this, users can implement their own compatible analysis types by creating a new AnalysisType and a new method for the same analyze() function.

Or, if you don’t like using types as values, a slightly different signature for analyze would be:

analyze(::Linear, x) = println("Linear analysis of $x")
analyze(::Quadratic, x) = println("Quadratic analysis of $x")

which you could use like this:

analyze(Linear(), [1,2,3])

(note the extra () after Linear in this case).

The Interpolations.jl package uses a design like this.

5 Likes

Some modules are just very generic and can be used across computing tasks in sub-modules, e.g. CUDA.jl if you want to utilize the GPU.