Working with Multiple Files in Julia#

Learning Objectives#

  • Understand how to organise Julia code across multiple .jl files.

  • Use the include() function to load external Julia files into a program.

  • Structure a small project by separating functionality into different files

  • Recognise when and why to use modules for better code organisation

  • Implement a basic module to manage namespaces and exports in larger projects

As your Julia projects grow larger, it’s good practice to organise your code across multiple files. Julia provides simple ways to include and reuse code from different files, allowing you to keep your code modular, maintainable, and easy to understand.

Using include() to Combine Files#

The simplest way to bring code from another file into your current script or notebook is with the include() function.

Suppose you have two files: helpers.jl:

function greet(name)
    println("Hello, ", name, "!")
end

main.jl:

include("helpers.jl")

greet("Alice")

When you run main.jl, Julia will read and execute the code from helpers.jl first, making the greet function available.

Note: include() simply inserts the code from the other file as if you had typed it into the current file.

Common Pattern:

  • Place helper functions in their own .jl file (e.g. helpers.jl), keeping your main script focused on high-level logic.

  • At the top of your main script, use include("helpers.jl") to load these helper definitions.

Example of Organising a Small Project#

File Structure:

my_project/
├── main.jl
├── setup.jl
└── game_logic.jl

setup.jl

function initialize_game()
    println("Game initialized!")
end

game_logic.jl

function play_turn()
    println("Turn played!")
end

main.jl

include("setup.jl")
include("game_logic.jl")

initialize_game()
play_turn()

When you run main.jl, it will include both setup.jl and game_logic.jl and call their functions.

Using Modules for Larger Projects#

If you want better organisation or to avoid accidentally clashing function names, you can wrap code in a module. A module is like a self-contained package of code. For example, MathHelpers.jl:

module MathHelpers

export add_numbers

function add_numbers(x, y)
    return x + y
end

end  # module MathHelpers

main.jl

include("MathHelpers.jl")

using .MathHelpers  # the dot means "from the current module"

result = add_numbers(5, 3)
println(result)

Avoiding Name Collisions with Modules#

Imagine you have two modules, each exporting a function called greet:

# File: English.jl
module English
export greet

greet() = println("Hello!")
end

and

# File: Spanish.jl
module Spanish
export greet

greet() = println("¡Hola!")
end

In your main script, you can include and call both without conflict:

include("English.jl")
include("Spanish.jl")

using .English   # import English.greet
using .Spanish   # import Spanish.greet

English.greet()  # prints "Hello!"
Spanish.greet()  # prints "¡Hola!"

Because each greet lives in a different module namespace, Julia treats them as entirely separate functions, avoiding accidental name collisions in large codebases.

A key part here is the leading dot in .English, which tells Julia to “look for a module named English in the current project rather than trying to load a registered package called English from the environment. In effect, this restricts the lookup to relative import: “I know I just did include(“English.jl”), so load that module”, avoiding any confusion with a hypothetical package called English elsewhere.

The Main Module#

Every Julia script or REPL session executes within a default top-level module called Main. When you write code at the top level, whether in a .jl file or interactively, you are defining names in Main. Any module X you include becomes a submodule Main.X, and a relative using .X refers to that. You can always refer explicitly to top-level names as Main.name, though that’s rarely needed.

*For more on standard modules, see the Julia manual Standard Modules Page.

Note: Modules are useful when your codebase grows and you want clear namespaces, controlled exports, or later want to turn your project into a package.

Good Practice:

  • Keep related functions grouped together in logical files

  • Name your .jl files meaningfully (e.g., plotting_utils.jl, data_loader.jl)

  • Start small with include() for simple projects

  • As your project grows, consider moving to modules for better organization

Exercise: Multi-File Projects#

Create two Julia files:

  • utilities.jl with a function double(x) that returns 2*x

  • main.jl that includes utilities.jl, and calls double(10), printing the result

Can you make it even cleaner by wrapping utilities.jl in a module?

End of Section Quiz#

What is the correct Julia keyword used to bring in code from another file or module into your current script?

After using include("filename.jl"), how do you access functions or variables defined in that file?