Love Julia, but..... Type hierarchy is frustrating

Two things that I have so much frustration with in Julia are importing and all the type and abstract type hierarchy. For example why does the following happen?
Capture

But you need this to be robust to do multiple dispatch, no?

Do you have some intermediate types there? This works for me:

julia> abstract type A end
julia> struct B <: A; end
julia> b = B();
julia> typeof(b) <: A
true
julia> B <: A
true
1 Like

That seems… unexpected. In general, your expectation is absolutely right that these two things should give the same output.

I can’t try this out because FEM doesn’t seem to be a registered package. One possibility is that the variable FEM in your session does not point at the top-level module Main.FEM. Another is that someone wrote a method

show(io::IO, ::Type{FEM.GenProp}) = .... 

that writes a “wrong” display value.

Both of these are not very typical situations. You can help us help you by giving more specific context.

(PS. please don’t post screenshots of code; you are excluding people who rely on screen readers and you are making it impossible for people who want to help you to copy-paste your code.)

13 Likes

I think @tkluck is onto something: two FEM modules are not necessarily one and the same. Main.FEM may be different from FEM.
EDIT: And actually, what my point really was, the types defined in those two modules which appear to have the same name but are not really the same, are also not the same.

3 Likes

Yes, I agree — this is most likely due to includeing your code files more than once. If you ever find yourself includeing the same file more than once it’s time to create a package so you can import or using it.

9 Likes

I was suspicious of this. I think this is definitely the case. So they come from the same module (which I wrote so this is highly suspicious as well). But I think one was instantiated through the REPL as opposed to coming from being imported into another module. I am still a bit unclear about using vs import and that things seem to possibly be in a different scope in the REPL. I will try to isolate it and see what is the issue and if not at least I will have a simple complete example to post. Thanks!

Yeah, it can definitely be confusing at first. The really important thing is that it’s totally fine to do using Foo and/or import Foo in as many places as you want–Julia will load the code exactly once no matter how many using Foo and import Foo you have. But include("Foo.jl") is more low-level: if you have include("Foo.jl") in two different places, you will end up actually including that code twice, potentially creating duplicate modules, types, and functions with the same names but different identities. This is a big difference from C++ where it’s common to #include Foo.h all over the place.

12 Likes

PHP had the same issue before and came up with the include_once function. Perhaps we could have something similar here?

This quote just came up in my mind: :slight_smile:

There’s already a package for that.

Why? Packages are a much better way to organize code.

1 Like

I agree. Packages are definitely nicer but I suppose that some people just want to write script.

In C/C++ people use preprocessor directives to avoid including things twice, as far as I remember (not sure whether things changed). Maybe a macro to be put on top of included files could do a similar thing?

1 Like

I really think include() should be banned from the REPL :see_no_evil: I saw so many horrible workflows, errors and conclusions related to this practice… I don’t know where this comes from, I guess this is something what you do in Matlab regularly?

'fully agree with @Tamas_Papp that packages are meant for organising code.

In Julia, the threshold to create a package is really low and I think everyone who does code development should learn how to do it.

Sorry for being off topic.

4 Likes

I use include() for two purposes, and I don’t see how I could avoid using include() in these two cases:

  • Scripting, i.e. to work on those pieces of code which are too application-specific for a package but too long to simply type it into the REPL.
  • Package development. Pkg.test() has a big overhead, and using Package requires to restart the REPL every time I change the package.

Have you ever tried Revise.jl? It takes care of reloading changed functions.

Regarding your scripting workflow, this of course totally differs from mine. I create projects and package for every “project” I work on, no matter if it’s an actual Julia package, a quick throw-away analysis or an serious scientific analysis. For the latter two I prefer DrWatson.jl.
Code which is “too long to simply type it into the REPL” are in my opinion functions which should have a short name :wink:

2 Likes

Code which is “too long to simply type it into the REPL” are in my opinion functions which should have a short name :wink:

I fully agree, and I’m working hard to make my students and collaborators adopt this point of view :smile:

A bit more precisely, I do write my scripts in the form of functions, but then I simply run include("file.jl"); fun(); to execute my scripts. If I understand correctly, Revise.jl would allow me to omit the include(), but for me the include() is not big enough of a deal to start using a sophisticated package which I don’t really understand. And another issue with Revise is that it does not allow me to redefine types, which is often a deal-breaker for me.

1 Like

Just FYI, I’m “using” Revise.jl, and have no understanding of how it works. I just put

@async begin
    sleep(0.1)
    @eval using Revise
end

in my startup.jl file, and never thought any more about it. To me, it just means that I don’t have to restart the REPL so much. There’s zero mental overhead in it to me. I’m actually slightly baffled when people suggest “using” Revise, since I cannot fathom why it’s not just in everyone’s startup file. I mean, what’s up with that?

5 Likes

This works until you observe something which doesn’t make sense to you, and then you have one more software component which could be the source of the unexplained behaviour. I remember running into such issues last time I tried Revise. These issues might have been due to Revise or due to something completely unrelated, but the point is that I don’t know and I also don’t want to spend the time to find out.

Anyway, I’m digressing into an unnecessary rant. The main goal of my posts was to hear and share how people use Julia, and me for one I learnt something, so thanks everyone!

I thought the recommended configuration for Julia v1.4- was

atreplinit() do repl
    try
        @eval using Revise
        @async Revise.wait_steal_repl_backend()
    catch e
        @warn(e.msg)
    end
end

to avoid loading Revise when using Julia non-interactively

3 Likes

I put that statement in there some years ago, and never thought more about it, so it figures that it’s a bit out of date.

I’ll update it now, thanks!