Yet another code organization question

Dear all,

I’m new to Julia, so first of alle hello :slight_smile:
I’ve already searched for similar question, but couldn’t find any answer that would solve my specific question. Sorry if I’ve just overlooked it.

So, let’s say I have some material models and parameter, like this:


module MyAwesomeMaterials

struct MaterialDataModel
    size
    elasticity
    resistance
end

ideal_material = MaterialDataModel(1, 33, 0)

real_material_1 = MaterialDataModel(2, 100, 2)

end

In truth, my data model consists of several structs with different constructors, and the actual instantiation is also rather involved (read: long), so I would like to split everything up into different files: One for the DataModel (the struct declarations so to say), and one file for each instance.: data_model.jl, ideal_material.jl, real_material_1.jl.

From what I’ve read in other threads/question, the recommended way would be something like this: Create a module file my_awesome_materials.jl with

module MyAwesomeMaterials

include('data_model.jl')
include('ideal_material.jl')
include('real_material_1.jl')

end

and basically cut and paste everything up there into their respective files.

The problem that I have with this is that the instance-files (ideal_material.jl, …) have no idea about the struct-definitions. In particular, the IDE doesn’t have any information and can’t help me with code completion and stuff like this… In C/C++ I would have the data-model in a header-file, include it in every instance-file and protect from multiple including via #ifndefs.

What is the most julia-idiomatic way to organize codes like this?

Thanks in advace and best,
welahi

2 Likes

Do you use VSCode? If all the files belong to the project autocompletion should work.

Getting intellisense (autocomplete) to work inside of modules has been very hit and miss for me. I use VS Code and have a method that mostly works.

The solution that has worked out for me is to first use PkgTemplates.jl to generate a package (it’s nothing that you can’t do yourself but makes it very easy when using interactive mode). Copy all of the code you want into the src directory and make sure all your files are included in the julia file with the name of the package, which has been generated for you.

I usually use a second environment to develop the package, created inside a subfolder called scripts. I run ] dev /path/to/package. This second environment is the one I activate in VS code. This way the intellisense seems to work. Although sometimes you have to reload the language server (can be found in the command palette) when I’ve added new code. Revise also works when you change code, just run using MyPackage again.

This method is way more complicated that I would like and I look forward to hearing from others who have streamlined this workflow.

Ok I’ve tried it with VSCode, it works, as long as I have the includes in the module-file. Then, VSCode can infer what I’m talking about even in ideal_material.jl. If not, it can’t (which would’ve been very surprizing if it could…).

However: Consider this edge case:

I have two different MaterialDataModel-structs with the same name (but different fields) in two different files, data_model1.jl and data_model2.jl. Further, I have two different modules MyAwesomeMaterials1 and MyAwesomeMaterials2 in two different files and one ideal_material.jl.
In MyAwesomeMaterials1 I include data_model1.jl and in MyAwesomeMaterials2 respectively data_model2.jl. Then, working in ideal_material.jl, it can’t be inferred by the editor which MaterialDataModel is meant to be used.

I know that this is a completely theoretical edge case, but I still think it should be possible to specify inside the ideal.jl-file uniquely, which other files it depends on. Besides that I’m not sure how many other IDEs have the same ability as VSCode.

Anyways, thanks for your reply!

Thanks for your reply! That sound indeed rather involved. I wish there was a more lightweight way… :S

1 Like

If you have the same struct name in different files, you should probably created a sub module for each of them, and only import the one you need at a time, this makes it very clear which is being used and works well with intellisense.

If you want dispatch to work on both of them, you can create an abstract type for both to inherit from and use that for your methods, and of course specialise on each when necessary.

True, the situation I’ve describe would be a very bad Idea in the first place.

You know what? I think the way Rust handles this is really neat and could also be adapted to Julia. There, you can just split module definitions over multiple files, e.g.:

// file1.rs
mod MyMod {
 fn a(...)...
 fn b(...)...
}

and

// file2.rs
mod MyMod {
 fn c(...)...
 fn d(...)...
}

In both files the same module-name is used and they are handled as if everything was written inside of one module-block in one file.

This way, I could wrap the contents of all files inside of a mod MyAwesomeMaterials and avoid the problem. Maybe I’ll open up an issue on github :stuck_out_tongue:

I think the equivalent in Julia is to have one file for the module definition, and then include the sub files with all the implementations. Auto complete also does work with this method, provided you have dev’d the module you are working on.

I use PkgTemplates to create the packages (it is very simple). And then develop the package in VSCode with a single module and includes, and that’s it. AFAIK it loads Revise by default (although I do have it in my startup.jl file).