Organizing structs and modules

I’m running into some trouble with organizing my files. I want to divide up my functions and structs into separate modules in separate files to keep them somewhat organized, and I don’t want to install them as Packages because I’m too noob. But some of my modules use structs that are defined in and exported from other modules. This is causing incompatibility when I try to use the structs in more than one module.

For example, let’s say module Fruit has struct Seed and function germinate().

module Fruit

export Seed, germinate

struct Seed
    # blah...
end

function germinate()
  # blah blah...
end

end

Then there is module Tree which includes that uses seed.

module Tree

include("Fruit.jl")
using .Fruit

export plant

function plant(s::Seed)
   # more blah...
end

end

But then I have another file Orchard.jl that wants to include and use both struct Seed and function plant, and if I make a Seed imported from Fruit, it won’t be recognized by the function in Tree if I pass it as an argument.

include("Fruit.jl")
using .Fruit
include("Tree.jl")
using .Tree

s = Seed()

plant(s)

This causes an error saying that it can’t find the function plant(::Seed).

I’ve been getting around this before by nesting all of my module files (like, Orchard includes Tree which includes Fruit), but this is getting to a point where it doesn’t seem very logical and I’d like to separate them.

Any insight would be very much appreciated.

2 Likes

There are quite a few related discussions on discourse, such as Large programs: structuring modules & include such that to increase performance and readability.

My (personal) suggestion is: unless the project is quite large, don’t use sub-modules at all. It just leads to exactly the kinds of issues that you are running into (how do I organize everything so that A depends on B but not the other way around? How do I make all types visible everywhere they need to be seen?).

Tastes differ on this question, though.

If you want sub-modules, you need one master module (probably Orchard in your case). Then orchard.jl includes the other modules (don’t include the same file multiple times). And if you need to see Seed in Fruit you do

module Orchard

module Seed
 ...
end
using .Seed

module Fruit
  using ..Seed
  ...
end
using .Fruit

end

Hope this helps.

7 Likes

Here’s another discussion which covers the exact same issue: Organizing modules. Is it OK to organize a project using several modules in Julia? - #5 by rdeits

The summary is, as @hendri54 said, that you should never be include-ing the same file more than once. Fortunately, you should also never need to do that if you structure your modules as suggested.

3 Likes

Thanks for clarifying that is the structure I am adopting.

Limitations of modules. The Julia manual states: Note that variable bindings can only be changed within their global scope and not from an outside module. which to me is problematic. https://michaelhatherly.github.io/julia-docs/en/latest/manual/variables-and-scoping.html

I have a large number of options using sub-modules for clarity (not shown). A simple example :

module option
     Number_Simulation = 100
end

Trying to modify option.Number_Simulation

module test
     import option

     function TEST()
         if option.Number_Simulation < 10
               option.Number_Simulation = 10
          end
    end
end

Nevertheless I get the following error: ERROR: cannot assign variables in other modules although I did not declare Number_Simulation as a const . Is there a way around to solve this issue? Many thanks for any help you may provide.

Just expose it via an accessor API. The following is more or less the idiomatic (type stable etc) solution:

module HasState
const THE_THING = Ref(0)
set_the_thing(value) = THE_THING[] = value
get_the_thing() = THE_THING[]
end
1 Like

Hi Tamas,
Thanks ever so much for taking your time to answer the question. Since I am still a beginner will you kindly provide to the community a working example, thanks :grinning:

I wonder if you missed the code in my reply, it is a working example.

Tamas is this the solution which you are proposing?

module option
     Number_Simulation = 100
     set_the_thing(value) = Number_Simulation[] = value
     get_the_thing() = Number_Simulation[]
end

Trying to modify option.Number_Simulation

module test
     import option

     function TEST()
         if option.Number_Simulation < 10
               option.Number_Simulation = 10
          end
    end
end

I still get the error message ERROR: cannot assign variables in other modules
Thanks for helping to correct the code.

You are missing key parts of the solution I propose (Ref), also, as I suggested, you should use the accessor API to set things, eg

using HasState
HasState.get_the_thing()
HasState.set_the_thing(4)
1 Like

Or, since you have many options, bundle them in a struct:

module test1

mutable struct T1
	x
	y
end

const t1 = T1(2, 3);

function set_x(z)
	t1.x = z;
end

function get_x()
	return t1.x
end

end

julia> using .test1

julia> test1.set_x(333)
333

julia> test1.t1
Main.test1.T1(333, 3)
3 Likes

Thanks that could be an alternative solution :smiley: