Changes of A.jl are not understood from B.jl that includes A.jl

Dear sijo,
I value and I appreciate your feedback, your thoughts, your time, and your motivation. However, you are not going to learn good programming practices by focusing on a one Language only (Julia). Some of the best ideas in programming (does not make sense to list them here) come from old Languages such as Turbo Pascal, ADA, Fortran etc. One really nice feature is ADA’s pre and post conditions. Look it up. All these features were introduced for promoting and ensuring safe and bug free coding practices. Julia is a new Language developed for Mathematics and Science so I assume it has many engineers bringing together practices from many different disciplines. However, allowing for same function redefinition is not a good practice for the reasons I already mentioned. A compiler error should be generated to promote a better coding practice and inform the developer.

Back to your question “how would that work if you get an error for redefinition”, simply it should not work. These includes are executed in the REPL. By the way I never liked the include("filename.jl") as a means to execute a script. It should be something like:
execute filename.jl or run filename.jl. I am sorry but include instead of execute or run is misleading and confusing since the include directive in C,C++ is just including a header file, and the word include here is used for executing a script. Totally wrong.

Could you clarify if my description of your workflow is wrong (the workflow that you were using in the top post)?

Because it seems to me you want to have the cake and it too:

  1. You want Julia to produce an error when a function is redefined.
  2. When you run include("B.jl"), then make changes to A.jl, then run include("B.jl") again you want Julia to use the updated code.

As I understand, in the second point you want Julia to accept your redefinitions, in contradiction to the first point.

2 Likes

You understood wrong.
If there are redefinitions Julia should stop compilation spitting an error saying:

"there are redefinitions in A.jl and C.jl. More precisely function Helper1(blablabla) is defined in A.jl line xxx, and also at C.jl line xxx. Compilation stopped because the result might not be the desired".

So, I go to my files, I rename Helper1 to HelperA1 in A.jl, and in C.jl I rename Helper1 to HelperC1. Then Julia will not find any redefinitions of the same function and should be able to compile that code without errors.

Is that clear for you now?

No this is not the redefinitions I’m talking about. I’m saying that when you change A.jl (in between calls of include("B.jl"), you’re probably changing the code of some function. Let’s say you update Helper1. When you do the second call of include("B.jl") you want Julia to execute this new version of Helper1. But from the point of view of the compiler, this second include is redefining Helper1. So in that case you don’t want an error!

1 Like

Julia actually does issue a warning for method duplication during precompilation of package modules which are cached to disk.

The following example shows the most succinct method for invoking this mechanism.

julia> push!(LOAD_PATH, pwd());
                                                                  
julia> write("A.jl", """
       module A
           f() = "Hello world"
           f() = "Bye world"
       end
       """)
59

julia> using A                                                    
[ Info: Precompiling A [top-level]                                
WARNING: Method definition f() in module A at 
/root/demo/A.jl:2 overwritten at /root/demo/A.jl:3.

ERROR: Method overwriting is not permitted during
 Module precompilation. Use `__precompile__(false)` 
to opt-out of precompilation.

[ Info: Skipping precompilation since __precompile__(false).
Importing A [top-level].
7 Likes

And this is why people stressed a MWE because C.jl wasn’t even mentioned in any of the summaries.

You still have grave misconceptions of how Julia works. Evaluating a method definition is not when the method gets compiled. Method compilation happens later for call signatures on demand, hence “just-in-time” compilation. As for why a method signature can be replaced by redefinition, it’s for runtime interactivity within 1 Julia process, a common feature of dynamic languages. An included C.jl defining the same method signature as a previously included A.jl is not distinguishable from includeing A.jl a 2nd time to replace that method signature. You clearly want to do the latter, so the former must be allowed. The only way to catch this is to develop within a package where its ahead-of-time precompilation (again, not the same as method compilation) must disallow method redefinition among many other things you can do instead at runtime.

Languages have different semantics and designs that affect the features and limitations of the implementations. A nice feature of one language can be fundamentally impossible or limited in another language that chose a different nice feature, in short a tradeoff. For example, referential transparency is a very nice guarantee for variables, but it makes syntactic loop structures impossible.

2 Likes

Julia is not C, so it would be ok if include meant something different from what it means in C.

However, this is not the case, #include in C is a preprocessor directive, which includes the entire file referenced in the body of the file containing it. The use for headers is conventional, nothing more. This is the identical semantics to Julia’s include, except for the part where include is an ordinary function executed during compilation, rather than part of a separate preprocessing pass. I hope this will make it easier to accept and remember how it works!

During runtime. An include call is compiled code and includeing a source file may trigger compilation of call signatures specified by the file, but the call does not occur within some method’s compilation even if the call is written in a method definition. That’s particularly important to clarify here because OP already demonstrated a misunderstanding of when compilation and precompilation happens.

Dear all,
the purpose of this discussion is to inform you that not spitting an error if the same function signature is “included” results in unexpected behavior or unintended behavior of the program being compiled. You are advocating above as if we are having Julia on trial. Clearly, if some language features result in unreliable code, unexpected code being compiled/executed without warning the developer etc, then this are not features promoting SAFETY which is the most important thing for engineering calculations. Julia is not Python or any other scripting language is a language for engineers and/or mathematicians that promotes at last a few nice features:

  1. Possibility of using source code and variable names as close to LaTeX as possible, God bless the soul that implemented this feature (may he is remembered to the ages of ages)
  2. A lot of packages allowing fast and quick integration of mathematical libraries
  3. Fast and easy prototyping
  4. Code that is compiled and not run as a script
  5. Automatic differentiation (even through libraries)
  6. Easy integration of a lot of packages and libraries and dependency resolution etc…
    And I am in the process of discovering more, but I would not sacrifice safety code integrity for whatever fancy and trendy informatic features. We already have enough to make our life easier. We do not need fancy stuff that go against the goals of using a Language for engineers such as Julia.

Please note that Julia is a dynamic language and thus allows you to have more freedom and interactivity. Personally I favor interactivity a lot more than the bit of additional safety given by more static languages. But taste may vary.
In your particular case the “unexpected behavior” can more easily be fixed by using a proper workflow/code organization and thus I don’t see it as a language issue. In your case, separating the “running” code from the “library” code more and putting the “library” code parts into a module would likely raised a warning (as @mkitti demonstrated).

I also want to comment on your earlier post which to me sounded rather condescending towards @sijo . Julia surely has shortcomings, may and should be critiqued and strive to improve. However you really should try to understand what Julia is and wants to be before you tell its community what nice feature from other languages they should adopt or how they should be doing things. Please try to be more open-minded and explore Julia’s design choices from a place of curiosity before you conclude that something needs to be changed.

6 Likes

Guys,
I am sorry, but as a user and developer, whenever I have created a mess including files with the same function signatures, I would like to get a warning or even better an error by the Language Compiler so that I can fix my mess. Is this so hard to implement? Isn’t this what a compiler is supposed to do? Warn you or stop you for creating a mess in your code at the early stages so you become a better developer and learn to adopt better practices? Isn’t this why compiler WARNINGS are implemented and exist, to teach you to code in a better and more safe style? What is the purpose for creating a new Language for engineers if it does not make sure that it protects the developer for messing things up? I am tired to argue about common sense.

Ah, but … this whole discussion centers around the workflow where you include a file containing many function definitions. Say file A.jl contains:

f(x) = x^2
g(x) = x^3
h(x) = x^3

Then you include("A.jl"), and all is fine. You discover a typo, edit A.jl to be,

f(x) = x^2
g(x) = x^3
h(x) = x^4

and include it again. Now you get three warnings, one for each of the three functions. If this A.jl is long, you would get a truckload of warnings. If at some place A.jl includes B.jl, in which you accidentally insert a g(x) = ..., you have got used to the truckload of warnings, and you would likely not notice this particular warning among all the others.

You should change your workflow. There are suggestions in the manual here: Workflow Tips · The Julia Language

4 Likes

Ah, but … this whole discussion centers around the workflow where you include a file containing many function definitions

No! Sure not! I included A.jl in B.jl, yes. BUT, C.jl was created by:
$ cp A.jl C.jl
$ vim C.jl
I modified the main function in C.jl, but I forgot entirely the last 2 functions of C.jl that had the same signature as those of A.jl. I entirely missed that because there was too many things to do in the Main of C.jl. Once I was done with the Main of C.jl I started running. I lost days until I realized why the results do not change no matter how hard I try to change the last 2 functions of A.jl under the Main of A.jl. Obviously after days I realized that C.jl had exactly the same functions as A.jl which I forgot to rename. If I got a compiler warning or error I would not have lost 3 days wondering or contacting you at this forum asking for your help and also wasting your valuable time.

Ok. You should then make an alias, e.g.

$ alias j='julia --warn-overwrite=yes'

and run your programs with

$ j A.jl
5 Likes

You’re not wrong for identifying advantages of other languages’ features that are lacking or limited in Julia. That’s a large chunk of this forum. However, we have informed you why it is limited, and incorporating the sort of effect you’d like would sabotage the “fast and easy prototyping” you listed as a nice feature. Something that makes prototyping faster and easier is not having to throw away the entire session to replace and test a method signature. If you throw errors when a method is replaced, you would have to. There is no practical way of distinguishing when you’d like to throw that error and when you’d like the method replacement to go ahead; changing the name of the source file you include does not matter to the global scope, and you had previously leveraged the ability to do that by renaming things _new.jl. Even if a reasonable strategy is invented, it would be a breaking change and cannot be incorporated into v1 Julia.

PSA: Julia is not at that stage of development anymore

I can’t say it’s easy, but plenty of languages have implemented errors. However, a language’s compiler can behave however the designers want. In fact, Julia’s compiler is not even involved when methods are defined, so it couldn’t possibly catch a redefinition and throw an error. The command for starting the Julia session provides an option for warnings, as demonstrated above, and you might also notice that it also occurs during package precompilation but goes on to trigger an error.

THANK YOU @sgaure. I am really grateful!

@Benny take a look at the answer of @sgaure above. He solved my problem. Thank you once again @sgaure.

1 Like

I am aware of sgaure’s comment, I directly referenced it. If sgaure’s comment resolves your problem, please consider clicking on the checkbox icon at the bottom of the comment to mark it as the thread’s solution. Doing so would show your appreciation of course, and the solution will be highlighted under your original post for future readers’ convenience.

The things is that there are many different workflows with julia. Most of them will find --warn-overwrite=yes highly annoying, since overwriting methods is a normal part of the workflow. I suppose that’s the reason why it’s not the default.

2 Likes

@sgaure, is there any way to permanently put this option in a .config file Julia is reading when you run it from within the REPL? If yes please let me know so that I can accept that as a solution otherwise I accept your previous answer as solution.