Huh, seems all Julia-nightly CI is broken now. I suppose this is a good example of the odd issues this will cause.
Run julia --color=yes "$GITHUB_ACTION_PATH"/add_general_registry.buildpkg.jl
[ Info: The General registry already exists locally
ERROR: MethodError: no method matching main(::Vector{String})
Closest candidates are:
main(; n, max_delay)
@ Main ~/work/_actions/julia-actions/julia-buildpkg/latest/add_general_registry.buildpkg.jl:53
Why not first check isapplicable(main, Vector{String}) before calling it? It would still break cases where there is a main function that expects an input, but would solve for the (Iād guess much more common) no-arg main problem
I had brought this up on the triage call a while ago: if we use __main__ instead of main for this, then this wouldnāt be a breaking change since __ names are reserved. It also helps to hint that something special and potentially surprising might be going onāotherwise itās a little strange that some regular function is just automatically called. The name __main__ being automatically called is a little less surprising and somewhat analogous to __init__.
I especially agree with using __main__ because it visually signals that there is potential weirdness, as with __init__.
You can guess that from just looking at it - why else would anyone use those underscores. With main there has to be some transmission of knowledge either from docs, error messages or someone else who knows.
This type of automatism is not needed in my opinion.
Actually (first) I would like to see a sufficient compilation to standalone apps before thinking about automating it. Second I want this feature in Julia itself and not in a package. Having this, I want (third) a reasonable easy workflow to create a not too large standalone app.
If this workflow is fine and satisfactory there is no need for automatism (or it is easy to be achieved).
Still, maybe, it needs to be defined now what the standardized way of entry-point must be (but I doubt it). In this case do it in a non-breaking way.
That would be my preferred option too from a userās perspective.
Were there any (convincing) counter arguments against the use of __main__ other than (from the PR; emphasis mine)
The existing __ function that we have __init__ is something that ideally users never have to use, whereas this is something that we expect people to use regularly. Of course, there is some possibility of unexpected behavior around the transition if some pre-existing main is called unexpectedly, but presumably that can be addressed by a sufficiently prominent note in the release notes.
I kind of see the point the first part makes, but tend to disagree with the emphasized part, and to be completely honest, find it a bit nonchalant. Why go that route when there are non-disruptive and backwards compatible alternatives?
But thatās not really a valid argument against __main__? I mean, if users already had to use __init__ we would have an argument in favor of __main__. It doesnāt follow that we should avoid __main__ because users donāt have to use __init__!
On the contrary, I think itās nice that users will know __main__, so when they look at package code and see __init__ they can rightly guess that itās called automatically.
Could this simply be called Main.__init__ ? its docstring seems to almost already fit with this new __main__ feature, except for the ARGS argument:
help?> __init__
The __init__() function in a module executes immediately after the
module is loaded at runtime for the first time. It is called once,
after all other statements in the module have been executed. Because
it is called after fully importing the module, __init__ functions of
submodules will be executed first.
[...]
(just an idea, not arguing in favor of it over __main__)
A normal-looking name, like main, with weird behavior seems very risky (especially a popular name like that.) A function with very special behavior, ought to have a special-looking name that signals that something out of the ordinary is going on, just like macros are set apart with the @ symbol.
__main__ seems like a serviceable choice, and even though Iām not particularly a Python fan, choosing the same convention right there is probably advantageous.
I would argue that __init__ is the wrong function name for this. __init__ by name does not specify that itās going to do anything other than initialize some things (which is how itās currently used), whereas __main__ sounds like itās going to run a whole bunch of application-specific stuff (which is how main, run, etc. are currently used).
I would argue that using __init__ is breaking, or at least how to use it if already using __init__ for another use is not obvious. I am using __init__ in a module for other one time initialization requirements. It seems to make much more sense to use a specific function name for this specific requirement so I think __main__ would be more straightforward.
Maybe I am wrong and would just have to add a few lines to the end of my existing __init__ function or add another __init__ function at another location.
āAs mentioned earlier, the whole idea of this change is that itās the first thing people new to the language will learn, so anything that relies on things that youāre not intending to talk about in the first three paragraphs of a āgetting started with juliaā tutorial isnāt gonna work.ā
Then I think a better explanation of what this is needs to be provided, at least for users like me. I donāt see me teaching this for any Julia new user, neither for relatively advanced ones. My impression is that this is for quite a niche use associated to people that require building compiled binaries for distribution, which will be always advanced users. I donāt see making this feature āsimple for new usersā of much importance really, much less breaking, if that is an issue.
My point was that __init__ can act like a main for a module today, without any changes. I think @rfourquet was also picking up on this same line of thought.
module Hello
function __init__()
# some common initialization code
abspath(PROGRAM_FILE) == @__FILE__() && main()
end
main(args=ARGS) = println("Hi", args...)
end
Notice that you can change the condition abspath(PROGRAM_FILE) == @__FILE__() to match any file, not just the current one.
$ tree Hello/
Hello/
āāā Project.toml
āāā scripts
ā āāā runme.jl
āāā src
āāā Hello.jl
$ cat Hello/src/Hello.jl
module Hello
function __init__()
# some common initialization code
abspath(PROGRAM_FILE) == joinpath(
dirname(@__DIR__),
"scripts",
"runme.jl"
) && main()
end
main(args=ARGS) = println("Hi", args...)
end
$ cat Hello/scripts/runme.jl
using Hello
$ julia --project=Hello Hello/scripts/runme.jl
Hi
$ julia --project=Hello -e "using Hello"
# <no output when used as a library>
What Iām confused about is why are putting more effort into Main the module to support system images rather than focusing on modules in general and taking advantage of pkgimages.
I honestly donāt see any real benefit in āunifyingā compiled and script workflows. Theyāre different things serving different purposes. Or if they are to be unified, you should make the compiled case more like the script-case, rather than vice-versa. The current semantics are nice ā itās just like typing the code at the REPL, or include()ing it ā read and execute the code, whether that execution is defining things or running. Changing that to āread and execute, and then execute another magic functionā just doesnāt seem helpful.
In order of preferences, Iād like
Being able to specify entrypoints in Project.toml, which gets us multiple for free, and lets us change exactly how that cashes out in terms of executables, etc.
The pre-merge status-quo until people have actually built out and explored the compilation space more.
__main__, which at least warns that this is something magical.
__init__, which isnāt the best name, but is already used for extremely similar purposes.
And finally, about ten further places down, snagging plain old main from user control.
(Note that 2. and 4. are extremely similar ā itās just a question of how much boilerplate needs to be included (2) vs happens automatically (4))