This strict dependency in declarations (that you see in C/C++) is not necessary in Julia. Having said that, I do find that simplifying your code to avoid circular dependencies is typically a good idea (whenever possible), as it makes your solution easier to read.
Going back to organizing your “module system”: In Julia, “software modules” are called “packages”, and I recently made a PR to help people get started:
→ FAQ: Creating your first package by ma-laforge · Pull Request #39186 · JuliaLang/julia · GitHub
But, to make it easier to read, here is a copy of it:
Creating your first Julia package
A quick way to start any new package is to create a skeleton with the Julia package manager:
pkg> generate path/to/my_package_repo/MyFirstPackage
path/to/my_package_repo directory should now have the following contents:
Using this method, a [UUID](@id man-code-loading-uuid) is automatically generated for the new package and written to
Testing/developing your new package
An easy way to make packages available to the active Julia environment is to add a package repository to the
julia> push!(Base.LOAD_PATH, "/abs/path/to/my_package_repo")
To automate this process and make the repository available for every new julia session, add the above statement to
pkg> generate automatically creates a
greet() function, you can call it after loading
MyFirstPackage with either
julia> import MyFirstPackage
Alternative: Testing/developing your new package
It is also possible to add new packages-under-development to a given project/environment using [
pkg> dev /abs/path/to/my_package_repo/MyFirstPackage](@ref Pkg). However, beginners might want to keep to the
LOAD_PATH solution until Julia [projects & environments](@ref man-code-loading-environments) are well understood.
Organizing package files
There are no strict rules on how to organize package source files, but the following is a good starting point:
│ ├── subcomponent1.jl
│ └── subcomponent2.jl
In this example, a “component” could be anything that warrants being in a seperate file. For example:
- A set of functions to operate on a given type (like an “object” definition).
- Code used to display objects of multiple types (ex: collection of
- A collection of type definitions.
- A given software layer (ex: the external interface intended for users of the package).
Here is a slightly more concrete example:
│ ├── FileFormatA.jl
│ ├── reader.jl
│ └── writer.jl
│ ├── FileFormatB.jl
│ ├── reader.jl
│ └── writer.jl
With such a file structure,
MyFirstPackage is assembled by loading code from individual files (using
include() statements). The following illustrates how this can be done:
#Import EXTERNAL packages required by solution:
using FFTW #Does frequency analysis
import Gtk #Needs a GUI (import avoids namespace pollution/collisions)
#Include INTERNAL project files themselves:
readAdata(filename::String) = FileFormatA.readdata(filename)
readBdata(filename::String) = FileFormatB.readdata(filename)
#Outside: Namespace is still "MyFirstPackage"
module FileFormatA #Preference: solution is cleaner with separate namespace
#Inside: Namespace is "MyFirstPackage.FileFormatA"
#Import EXTERNAL packages required by this "sub-module":
using DataFrames #Build on readily available reader/writer
#[Probably add type definitions here]
#Include INTERNAL project files themselves (file-relative):
On a conceptual basis,
src/FileFormatB/FileFormatB.jl would be similar to