(on hold): If you have a function called `main`, you may need to tweak it

EDIT: The change is on hold for the time being.

Yesterday I merged https://github.com/JuliaLang/julia/pull/50974, which standardizes the pattern

function main()
# stuff
end
main()

The idea here is that by standardizing what the entry-point looks like, future iterations of (Package-,Static-,etc)Compiler will be able to automatically turns scripts with these patterns into compiled applications. However, as a result, if you are currently using this pattern, you will have to adjust it to the standardized variant.

The most common change that is required is:

  1. The standard version takes ARGS, so you’ll have to add that to your function signature:
function main(ARGS)
# stuff
end

Base.ARGS continues to remain available, but it is recommended to switch to the passed ARGS instead for future compatibility (if you’re doing argument processing at all - if not you may ignore the argument, but you should still have it in your signature).

  1. The main function is called automatically. If you’re living on master, that means you can simply delete the call to main. The julia driver will call it for you. If you want to support both versions, the recommended incantation is:
!isinteractive() && VERSION < v"1.11.0-DEV.403" && exit(main(ARGS))

I recognize that there may be some inconvenience and breakage in scripts, etc. due to this change, but hopefully it’ll be quick to fix and enable a smooth transition to fancier workflows in the near future.

22 Likes

This is neat and useful, but is this not a breaking change that goes against the “spirit” of semantic versioning? I know this question has come up before – I have tried to read through the core developers’ comments in previous threads in order to learn the “flavor” of semver that they have adopted for julia, but this particular change still surprises me. Is there a more formal reasoning here than “by our estimate, this is sufficiently painless of a breakage with sufficiently big upside”?

To be fair, this reasoning seems good enough to me as the upside is obvious and the breakage is indeed minor (I have experienced way worse breakages in python and its ecosystem, for instance), but some more formal description of the reasoning will be helpful in building my mental model of what semver means for julia.

10 Likes

I must admit, I don’t understand why ARGS can’t be optional, and reading the PR hasn’t clarified this to me.

2 Likes

Technically any added function or behavior could be considered breaking because there might be a pre-existing function by that name somewhere.

Minor versions according to https://semver.org/ are meant for added functionality that is backwards compatible.

@odow argues here that is actually a non-backwards compatible breaking change:

Edit: My comment in summary is that I think this should be Base.main rather than Main.main.

That would be backwards compatible since you have to import Base.main to use this functionality.

5 Likes

I see ~400 results for function main() here: grep.app | code search

4 Likes

I think this is going to catch many more people off guard once 1.11 is released, potentially for years to come when they rerun old code (or just think of someone trying to reproduce some paper… :grimacing: ). It’s unfortunate that the breakage was not anticipated, but as a PkgEval would have caught this, it’s a shame that we’re now going to have broken PkgEval until this is solved one way or another.

2 Likes

I would absolutely consider this a breaking change, but having Base.main instead of Main.main seems like it would perfectly solve the problem, and be a really nice feature.

7 Likes

I think PkgEval is gonna underestimate on this one because many main() live in scripts, not in registered packages

25 Likes

Could we throw a very informative error if the main signature doesn’t match the new format and add a command line flag for running without the new entry point? Then there would be an easy path for later to get the old behavior back when running old code. Something like --nomain or so.

1 Like

I replied to this on the GitHub issue to avoid bifurcating the discussion.

Yes, this is easy to do.

The first bit is probably intended to be !isinteractive().

Yes, sorry formatting issue. Will edit the OP.

1 Like

That was also my first impression but now I think it would be a confusing hack: this main is really about Main, not Base, and it’s not a regular method. So Base.main would be misleading.

Here’s what Keno wrote in the PR:

This is not workable, because this is designed to support Main.main bindings that are things other than methods of a generic functions. It also breaks the julia -e 'using MyApp' thing, because it requires type piracy and if some other package also has a main function, suddenly you’ve broken precompile.

Also, “making compilation first class” is a huge deal and a massive win for the language, and first class means main rather than something “convoluted” like Base.main.

3 Likes

Looking around I see that other languages are using configuration files to declare binaries and entry points.

See the [[bin]] section in Rust’s Cargo.toml:
https://doc.rust-lang.org/cargo/reference/cargo-targets.html#binaries

Python has even moved in this direction as well.
https://packaging.python.org/en/latest/specifications/declaring-project-metadata/#declaring-project-metadata
https://packaging.python.org/en/latest/specifications/entry-points/

While not mutually exclusive, perhaps we should consider if this would be better suited to be declared in (Julia)Project.toml instead of the language?

7 Likes

There is a gigantic thread What steps should the Julia community take to bring Julia to the next level of popularity? elsewhere.

My reply would be: Don’t casually break user code here when there are fine alternatives (such as using __main__ instead of main).

12 Likes

Just so everyone is on the same page, @Keno what do you consider on topic here? IMO, Discourse is a much better place to have such (potentially) long, diverging discussions with lots of opinions, so it would be better to discourage people from having this general discussion on the Github PR, and keep the PR comments to specifics about this implementation. Either this Discourse thread or a new thread split away from this would be a better place for the “is this breaking and how do we mitigate that” discussion.

I started one with a proposal.

1 Like

Well, kind of depends on where the GitHub thread is going - if the change is getting reverted, then there’s broader discussion to be had there. I guess for the moment, that means concrete technical suggestions about what to do instead go on GitHub, general griping about my cowbody attitude can remain here and personal attacks can remain in the usual social media channels :wink: . I realize that I probably set the wrong precedent for that by replying to the question about breaking changes on GitHub.

8 Likes

Just to point it out here too, with a good warning message, the compromise suggested by @Keno seems like a good way forward to me.

I disagree that __init__is like main. Using C as a comparison, it would be __attribute(constructor)_, while main is main. One is present in all executables while the other is a special behavior that isn’t super common.

1 Like