Unending error message upon including script1 in script2 AND vice versa at the same time

I included script1.jl in script2.jl and script2.jl in script1.jl because both contained structs required in both the files. I got an unending “LoadError: LoadError: … : LoadError: StackOverlowError”.
This makes sense but I think there should be a better way to handle this I had to force quit the REPL(Ctrl ^ Z).

Probably the same as https://github.com/JuliaLang/julia/issues/24592, although there it ends after 80000 lines of error.

I think the interdependent objects from the files can be pooler and then logically formed into a hierarchy by the compiler. I mean just throwing a single error message is enough since the problems can be solved by distributing the objects in the two files differently. But this would be cool!

Can someone tell me how to solve this? I have to define the interdependent mutable structs in order plus now I have to keep them distributed. But I still can’t skip the looping error since they end up making a loop or being defined in the wrong order…

So you’re question is about how to have mutually circular type definitions, and not about the error (as your first post suggest)? If so, have a look at handle mutually-circular type declarations · Issue #269 · JuliaLang/julia · GitHub.

The solution given there, which is to use a begin block doesn’t work either. And no I wanted to report the error problem.

If two things keep calling each other forever, it’s a stack overflow… now sure what else could possibly happen here.

Can we reduce the very big error message?
And since begin block isn’t working, it boils down to not specifying the actual precise data type that leads to the circular nature. Can we do something about that? Modelling real objects with optimisation is difficult without it. Even creating algorithmic objects. Unless you specify an additional abstract type to encapsulate the other. :thinking:

Why don’t you add your example to the issue I linked above, or if you think it’s different open a new one?

Regarding the types, you can use parameters as a work-around:

struct Foo{T}
   bar::T 
end
struct Bar{T}
   foo::T 
end

x-ref: https://github.com/JuliaLang/julia/issues/269#issuecomment-68421745

Okay. Thanks

I did that by creating an abstract type. Eg:

abstract type SomeType end
mutable struct One <: SomeType
two::SomeType
x::Bool
end

mutable struct Three
ones::AbstractArray{One}
end

mutable struct Two <: SomeType
threes::AbstractArray{Three}
end

Note that One.two has non-concrete type (see performance-tips in docs). Thus, if you use it somewhere performance critical better do:

mutable struct One{T<:SomeType} <: SomeType
two::T
x::Bool
end

It is throws an error. :confused:

LoadError: LoadError: invalid redefinition of constant One

I did this instead, seems to work.
Will this still be good for performance?

mutable struct One <:SomeType
    two::T where T<:SomeType
    something::Float16
end

Start a new REPL.

The other alternative you show below is not concretely typed, so it won’t help.

Reading through the manual, at least the types part and the performance FAQ, may be helpful at this stage.

2 Likes

Reading through the manual…

I can recommend that too!

You can check whether Julia can infer types like so (slightly simplified your example):

julia> abstract type SomeType end

julia> struct One <:SomeType
           two::T where T<:SomeType
           something::Float16
       end

julia> struct Two <: SomeType end


julia> f(t) = t.two                                                                                                                                                      
f (generic function with 1 method)                                                                                                                                       

julia> @code_warntype f(One(Two(), 5))                                                                                                                                   
Variables:                                                                                                                                                               
  t::One                                                                                                                                                                 

Body:
  begin                                                                                                                                                                  
      # meta: location sysimg.jl getproperty 8                                                                                                                           
      SSAValue(1) = (Base.getfield)(t::One, :two)::SomeType                                                                                                              
      # meta: pop location
      return SSAValue(1)                                                                                                                                                 
  end::SomeType                                                                                                                                                          

This shows that Julia doesn’t know the concrete return type, only that it must be <:SomeType.

1 Like

Thank you @Tamas_Papp and @mauro3
This has been very helpful indeed.

Also should I use

mutable struct A{T<:Parent} <:Parent

for all structs under Parent or just for the structs which directly use another struct from the same Parent?

Please do read the manual thorougly and get back here if you have questions. At this point answering this would just repeat sections from the manual.

Thank you.