Where do you put your "main" function in your projects?

I’ve been working on a side project to convert an old fortran program to Julia (mostly as an escape from working on my thesis, and an excuse to learn Julia) and recently have started experimenting with encapsulating the whole effort into a module/project.

The whole effort is now a project.toml w/ associated module that contains all the transliterated fortran subroutines. Within the root project dir, I have a main.jl that is basically

using mymodule

function main()
   # Transliterated fortran main subroutine
end

main()

Just curious if this is SOP for a Julia project, or if there’s a more idiomatic/common approach to locating the “main” function?

2 Likes

Welcome to Julia!

You have it about right.

You can use PkgTemplates to set up a standard package structure for you.

For example:

using PkgTemplates
generate("MyPackage", Template(;user="My Name", dir=".", dev=false))

will create the following directory structure:

.
├── LICENSE
├── Manifest.toml
├── Project.toml
├── README.md
├── src
│   └── MyPackage.jl
└── test
    └── runtests.jl

With src/MyPackage.jl:

module MyPackage

greet() = print("Hello World!")

end # module

Of course, you can replace greet with your main function.

3 Likes

Thanks! So a user who is using the package as a monolithic “program” would just open up a Julia repl, start the session with import mymodule, and then fire off a

> mymodule.main(args...)

?

I suppose there’s also nothing stopping me from retaining an explicite entrypoint with something like run.jl: using mymodule; main(). I guess one nice thing about the former approach though is that it abdicates the programmer from having to have a friendly UI for finagling the users data into/out of the workspace (and hence deciding on a dedicated file format, allong with having a system for IO).

That’s a possibility. Standalone Julia programs are not that widespread at this point, so there is no “standard” solution for running them.

You can provide a MyModule.main(args...) (note: we usually capitalize module names), and then have a run.jl which parses command line arguments and calls this function, if you want to run it from the shell.

6 Likes

I think it depends on whether you expect users to compose your code/algorithms/types with other julia code, or if you expect it to be more of a grey-box command line tool. Most bioinformatics tools are more like the latter, and I’ve been experimenting with a bin/ subfolder that contains a script that calls the main function (using ArgParse.jl for a nice interface).

Of course, there’s also no problem with making it accessible from the REPL instead (or in addition), as @Tamas_Papp mentioned there isn’t a ton of this sort of thing in the julia ecosystem yet, so a standard has not really emerged.

3 Likes

Well, having a usable API would be nice, but tbh that ship sailed the moment I decided I was going to re-implement the fortran API (although if this ever goes beyond being “an excuse to have a personal Julia project”, I would probably go back and re-do a lot of things to be more generally usable).

As an aside, I see the lack of both options (usable APIs and also more modern cli tools) to be a pretty big weak point in this particular field. The fortran code basically solves a transfer function estimation problem, with the only usable cli offerings being circa early 2000’s, using early 2000’s algorithms (which are probably mostly sufficient tbh). I have seen attempts in the past few years to offer python “toolboxes” that present frameworks for solving the problem, which is nice and flexible, but also doesn’t help if you really just wanted a greybox cli tool (and when the framework starts to get monolithic with it’s own terminology, configuration files, data formats, and idioms, it starts to feel like it would have been more useful to have a well written cli tool that could be easily dismantled if you needed to do something custom).