What is the preferred way to use multiple files?

I wanted to structure my code by dividing it into several files. But I ran into some problem. So, in its simplest form, I have 3 files:
main.jl

module main

include("structs.jl")
include("functions.jl")


first_point = structs.Point(1.5, 1.5)
print(functions.distance(first_point))

end

functions.jl

module functions

include("structs.jl")

export distance


function distance(point)
    return (point.x^2+point.y^2)^0.5
end

end

structs.jl

module structs

export Point


mutable struct Point
    x::Float64
    y::Float64
    Point(x, y) = new(x, y)
end

end

And when i try to run main file I get this error:
ERROR: LoadError: MethodError: no method matching distance(::Main.main.structs.Point)
Closest candidates are: distance(::Main.main.functions.structs.Point)
So what’s the problem?

What julia version are you on?
Have you run your code on a clean julia session?
On 1.2.0 your code seems to run.

I have Version 1.2.0 (2019-08-20)
Just now restarted the session - the result has not changed

You are including structs.jl twice. Is that really what you want?

2 Likes

So I should replace my main file by something like that?

module main

include("functions.jl")


first_point = functions.structs.Point(1.5, 1.5)
print(functions.distance(first_point))

end

Alternatively remove the include statement from functions.jl
and only include structs.jl from main.

That depends a little bit on your desired program structure.
Do you want doubly nested modules by making structs a submodule of functions which itself lives in main?

Alternatively both functions and structs could live in main directly.

Note however that all these modules are not necessary at all in your case here. In particular your main module does not seem to serve an purpose.

2 Likes

Maybe you know this, but Julia doesn’t care what files your code lives in, nor what folders those live in, include is just like pasting the code into that file. (I forget whether there are edge cases, but this is the rough idea.) So you can split or combine them in whatever way you like.

But it does care about module a lot. And generally you shouldn’t be making sub-modules within other modules without a good reason. You appear to have two sub-modules main.structs and main.functions.structs which just happen to be defined with identical source code.

2 Likes

I’ve ran into the same problem.
My solution was to make Functions and Structs modules aware of each other.
main.jl:

module main

include("structs.jl")
include("functions.jl")

...
end # module

structs.jl:

module structs

using ..functions : ...

...
end # module

functions.jl:

module functions

using ..structs : ...

...
end # module

That way, functions loads structs module from the main namespace, and the error goes away. The downside is, of course, that modules functions and structs cannot be used in a stand-alone fashion.

1 Like

I think, that I misunderstand, when there is necessary to create modules, because I have not met this in other languages. With the help of modules I can hide some functions from user. I have only one analogy - private methods and private attributes in OOP languages, but it isn’t good analogy

If I am not mistaken in C ++ include works in the same way

1 Like

Yeah, this could be the case.
In my eyes modules are never necessary and in julia there is no way to truly hide functions from a user.
One typically exports public API but one can always access nonexported functions via MyModule.functionname.

Modules can be used to group various function/type definitions into a (potentially) separate name space
that allows you to easily include and reuse them.

As an example: In research projects I have one module that contains all my utility function definitions and machinery that I use in multiple places. My scripts load that module and work from there.

2 Likes

In julia, as a rule, don’t use submodules.

Namespaces are overrated, lets do less of those.
It is a bit of a mind-twist, I know.
Took me a while to get use to it.

Goal is to seperate functionality that is independent.
If it is truely independent, then it can be its own package.
If it isn’t then it can just go in the main module of the package.
Submodules work really poorly if the code isn’t independent, as importing things from the parent or sibling namespaces has to be done explictly.

Some valid uses of submodules modules:

  1. You think it is independent, but you are not sure yet. Then a submodule lets you try it out. If you can indeed do it without suffering and importing from the parent namespace then it is independent, and you can later make a new package for it when you have time.
  2. as a hack to allow all your enums to be referred to as EnumMod.enumval, by just putting the @enum as the only thing inside module EnumMod.
  3. You are writing julia itself and making a submodule of Base, thus your parent module is inscope because it is Base and Base's exports are always inscope. Also you don’t have the option of a standalone package (or even a stdlib) as Base can’t depend on those.
6 Likes

While I agree with most of what you say, I’m not sure about this bit. Well, I do agree that independent sets of features can go into separate packages, but why would they have to? If a given piece of code provides functionality that is independent, but so specific that it is only ever going to be used in a single application, why bother separate it into a package?

Developing two packages at the same time, one depending on the other, is much more cumbersome IMO than having two submodules in the same package, one using or importing the other. For example, when there is only one package, there is only one set of sources in a git repository, which means you don’t have to worry about which specific version of your application package depends on which specific version of your library package.


I fully agree with that, but I would add: “you can later make a new package for it when you have time and you have a use for this set of functionality in an other context”.

3 Likes