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.

3 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

I know that others have solved your problem and that this is an old thread, but no one really explained

  • how you should solve the problem
  • why it works

Instead, you were provided with a few solutions. I assume they work, but they do not address your fundamental problem.

In the code you have shown, you provide an example type and an example function. Those two things are very tightly linked. The two concepts depend on each other, and there is an important ordering between them.

Let’s explore this

  • first you define a type (a point in 2d space)
  • then you defined a function which can operate on that type (something which can calculate the distance to the origin)

Two things to observe:

  • the type can exist without the function
  • the function cannot exist without the type

Hence we conclude the function depends on the type. The type does not depend on your function.

Despite the above, the two are very closely related concepts. This strongly suggests they should be in the same file, and certainly the same module.

Put another way, would you publish this package with the type by itself?

The answer is no, because the point type does not have much use without the associated distance-to-origin measuring function. The reason is that you are providing clients of your library a function to measure the distance-to-origin, you are less interested in providing them with a type.

If you follow the rules implied by these design concepts, you will automatically solve the problem by putting everything in a single file.

  • I understand that you chose, deliberatly, to split the function and the type into two files to demonstrate a problem, but in this context it doesn’t make sense for the reasons provided above

Your enthusiasm is good, but you should know that reviving an old thread, especially one that has resolved after significant activity, is highly discouraged as a social custom; some discourse forums actually lock threads after a period of inactivity, but Julia’s is a bit more flexible. The usual approach is to start a new thread and link the old thread with a summary; if the intent is to address someone in particular, a private message can be sent. I can also recommend experimenting in the interactive REPL, especially since you started a somewhat related thread about include yesterday. This will often prevent basic mistakes such as:

To illustrate, we can evaluate said function right into a fresh session without the type. It not only successfully exists, it works on types that weren’t even brought up:

julia> function distance(point)
           return (point.x^2+point.y^2)^0.5
       end
distance (generic function with 1 method)

julia> using StaticArrays

julia> distance(SVector(1, 2))
2.23606797749979

You missed the point I was making. Here, your function takes an Any. It doesn’t take a Point2D

I should have been more explicit - what I was suggesting was the function distance should have taken some kind of concrete or abstract type which is defined by the example module (package) shown in the first post.

I know that this Euclidian distance measuring function is a generic concept, but for the purpose of the point being made just imagine that it were something else, specific/niche which doesn’t make sense without the accompanying type(s) created in the same package. This is how most packages work - you introduce something new not a generic formula/algorithm.

Of course it could be you write a package of generic algorithms but then what I suggested would not apply for the reasons you gave.