Module loading problem

Hello there

I’m trying to understand how to use modules probably, but I’m finding the way they work and how they are loaded kinda messing. The following example yields would I expect on the first run, but every subsequent run, I get several warnings

module Mstruct

    export point2D, point3D

    mutable struct point2D
        x::Int32
        y::Int32
    end

    mutable struct point3D
        x::Int32
        y::Int32
        z::Int32
    end

end

module M1

    using ..Mstruct
    export add2D

    function add2D(p::point2D)

        return p.x + p.y

    end

end

module M2

    using ..Mstruct
    export add3D

    function add3D(p::point3D)

        return p.x + p.y + p.z

    end

end

using .Mstruct, .M1, .M2

function main()

p1 = point2D(1,1)
println(typeof(p1))

p2 = point3D(1,1,1)
println(typeof(p2))

val = add2D(p1)
println(val)

val = add3D(p2)
println(val)

end

main()

First run I get:

point2D
point3D
2
3

but every following run yields:

WARNING: replacing module Mstruct.
WARNING: replacing module M1.
WARNING: replacing module M2.
WARNING: using Mstruct.point2D in module Main conflicts with an existing identifier.
WARNING: using Mstruct.point3D in module Main conflicts with an existing identifier.
WARNING: using M1.add2D in module Main conflicts with an existing identifier.
WARNING: using M2.add3D in module Main conflicts with an existing identifier.
point2D
point3D
2
3

Now I understand this is happening because the workspace has already loaded the modules, but how do I avoid this? Naturally its not a problem, if I “Stop REPL” between every run, but I would definitely prefer not to have to do that. On the whole I think “why” is more important to me than “how do I fix it”, as I find modules general unintuitive.

Another problem I have been having, which i think comes from the same lack of understanding, is that when I define the input of functions in modules (for example here, where add2D(p::point2D)), the base, and module name gets appended to the struct, so the function fails to run, because the objective is defined as “Main.Mstruct.point2D” and not as “point2D”.

Take a look at Home · Revise.jl

On the whole I think “why” is more important to me than “how do I fix it”, as I find modules general unintuitive.

You answered that question by yourself already, e.g.

Now I understand this is happening because the workspace has already loaded the modules

I did not understand the last question.

I don’t think I did tbh. Loading regular packages (which I assume consists of modules) does not have this problem. I can run my environment as much as I like, while using DataFrames, without being warned that its already loaded. As such I assumed there had to be a way to get the same behavior from my own modules.

The last bit was a bit of a tangent and I should probably not have included it.

How are you running your code?

See the Revise workflow below.

https://docs.julialang.org/en/v1/manual/workflow-tips/#Revise-based-workflows

Per REPL session, you only need to do the following once. Changes to method definitions should be updated automatically by Revise.

julia> using Revise

julia> using Mstruct, M1

The larger issue is probably code organization. Generally, the package I would be developing would be in a file with one overarching module carrying the same name as the package. That module may have submodules.

See the manual section on code loading.

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

If you want a template try PkgTemplates.jl.

Yes, loading the namespace is no problem but you are redefining the module.
In principle you can make it work without revise by only running the code that defines the module once. I believe you should be able to use using MyModule as much as youd like.
But if you want to change anything in your module you should definitely use revise.

Thanks for both the replies, and I’ll definitely give it a look Mkitti. The way I run it currently, is just in VS code with the “execute active file in REPL”. I’m working with some rather big MIP models using JuMP, and its very much just prototyping, so I’m not building anything general, or meant for wider use. I merely thought putting some code into modules would tiddy up the workspace a bit, and becoming more familiar with Julia seemed like a nice bonus.

I found a work around, which is to move the modules into several files (which I would do anyway, but didn’t in my toy example), and wrap the include in an if statement, so the includes is only called, if the modules don’t exist in the workspace already.

Still, you will have a better experience using Revise. Take a look at Development workflow · JuliaNotes.jl.

Then, instead of having to reload the complete module, you’ll just rerun the modified functions.

In Julia the barrier between development for personal use and exploration and development for distribution is so small that it may be practical to develop actual packages whenever the code starts to be more than a few functions. You don’t need to register the package, but it is still useful to have them properly strictured even if for personal use.

Sounds great, but what you linked me, doesn’t really help me get there. One thing to point out, is I’m a math student. While I have had courses on data structures and analysis, probably code structure (software development?) isn’t something I have been taught or had the time to delve into. Its a hole that I feel often, but when all you do is chase deadlines, finding the time to tinker with this sort of thing is hard.

Very directly, do the following: Structure your script like this:

import Pkg
Pkg.activate("myscript",shared=true)
module A
    export Point2D
    struct Point2D
        x::Float64
        y::Float64
    end
end
module B  
    using ..A
    export add
    function add(x::Point2D,y::Point2D)
        Point2D(x.x + y.x, x.y + y.y)
    end
end

In Julia, add Revise (one time only):

import Pkg; Pkg.add("Revise")

then, load your script with includet (note the t) and use your modules:

julia> includet("./script.jl")
  Activating new project at `~/.julia/environments/myscript`

julia> using .A, .B

julia> x = Point2D(1,1)
Point2D(1.0, 1.0)

julia> y = Point2D(2,2)
Point2D(2.0, 2.0)

julia> add(x,y)
Point2D(3.0, 3.0)

Now, if you modify the the function add, for example, that will reflect directly in its execution. For instance, I modified it (and saved the file, of course), by making it only sum the x coordinate:

# (inside script.jl - modify and save)
module B
    using ..A
    export add
    function add(x::Point2D,y::Point2D)
        Point2D(x.x + y.x, 0.0)
    end
end

Without doing nothing else, the add function now is updated in the running Julia section:

julia> add(x,y)
Point2D(3.0, 0.0)

ps: The question if you need more than one module or not is another one. Probably you don’t, most Julia packages have a single module. Within that module you can include other files with code that specify parts of what you want using the standard include("./struct_definitions.jl"), for example.