Too new for this world problem when called from a function

Dear all,

As a part of a program I am developing in Julia, I would like to have boundary conditions defined as Julia functions. However, I ran into the “too new for this world problem”. It took me quite a while to come up with MWE. (It may still be a bit on the big side, but anything smaller simply wouldn’t feature the error I am getting in the real program.) Hence, apologizing in advance that it may still be too long, here it comes:

module BoundaryMod                                                               
  export Boundary                                                                
                                                                                 
  mutable struct Boundary     # structure with boundary condition definition        
    value :: Function         # holds functions describing boundary conditions   
    function Boundary()  new((x,y,z) -> 0.0)  end                                
  end                                                                            
end                                                                              
                                                                                 
module StagMod                # module holding numerical method                  
  export Stag                                                                    
  export stag_allocate        # allocates memory for the method                  
  export stag_compute         # compute something based on boundary conditions   
  using ..BoundaryMod                                                            
                                                                                 
  mutable struct Stag                                                            
    b :: Boundary             # needs boundary conditions                        
    function Stag()  new(Boundary())  end                                        
  end                                                                            
                                                                                 
  function stag_allocate(s :: Stag)                                              
    s.b.value = eval(Meta.parse("function(x,y,z) return x+y end"))               
  end                                                                            
                                                                                 
  function stag_compute(s :: Stag)                                               
    println(s.b.value(1.0, 1.0, 1.0))                                            
  end                                                                            
end                                                                              
                                                                                 
using .BoundaryMod                                                               
using .StagMod                                                                   
                                                                                 
function main()     # calling this creates "not from this world" problem         
  s = Stag()                                                                     
  stag_allocate(s)  # allocate memory and define boundary conditions             
  stag_compute(s)   # compute something with it                                  
end

If I load this into Julia and run main(), it creates the error:

ERROR: MethodError: no method matching (::Main.StagMod.var"#1#2")(::Float64, ::Float64, ::Float64)
The applicable method may be too new: running in world age 29641, while current world is 29642.
Closest candidates are:
  (::Main.StagMod.var"#1#2")(::Any, ::Any, ::Any) at none:1 (method too new to be called from this world context.)

Funny enough (or maybe quite logical, just beyond my understanding) if I load this file and run three functions from the main() from REPL, they behave just fine:

julia> s = Stag()
Stag(Boundary(Main.BoundaryMod.var"#1#2"()))

julia> stag_allocate(s)
#3 (generic function with 1 method)

julia> stag_compute(s)
2.0

Please let me know if you can give me a hint on how to resolve this.

Cheers

What you see is called world age problem which also hit me before.

The question is: Why do you need Meta.parse?
I mean, you can define your function like always and just pass it as a variable?

Well, no. In reality the stag_allocate reads boundary conditions from a file and parses them into Julia functions. (I know this is not a good practice because it is anywhere from unsafe to downright dangerous, but I think it is easier for future users to change one line in a separate input file than to fiddle with the rest of the code.)

I think, it would be better to parse those boundary conditions as variables (probably arrays?) instead of baking them into hard code.

You can still execute those functions but then you have to use Base.invokelatest.

3 Likes

I was trying with Base.invokelatets but it didn’t lead me anywhere, really. The same error would still appear, but precisely at the call to Base.invokelatets. (Maybe I was doing something wrong.) Arrays would work for steady boundary conditions, but very often they are time-dependent, so one has to recompute them at the beginning of each time step.

julia> function wa()
           g = eval(Meta.parse("(x, y) -> x^2 + y^2"))
           Base.invokelatest(g, 1, 2)
       end
wa (generic function with 1 method)

julia> wa()
5

julia> function wa()
           g = eval(Meta.parse("(x, y) -> x^2 + y^2"))
           g(1,2)
       end
wa (generic function with 1 method)

julia> wa()
ERROR: MethodError: no method matching (::var"#17#18")(::Int64, ::Int64)
The applicable method may be too new: running in world age 29664, while current world is 29665.
Closest candidates are:
  (::var"#17#18")(::Any, ::Any) at none:1 (method too new to be called from this world context.)
Stacktrace:
 [1] wa()
   @ Main ./REPL[25]:3
 [2] top-level scope
   @ REPL[26]:1

Yes, bet recreating new source code is even worse?

Each time, you would have an additional compilation lag.

If those numbers are passed as parameters, you compile only once.

Well, that’s not the way I understand Julia works. You read boundary conditions once, compile functions describing boundary conditions also once, and for the rest of the simulation you use compiled code. Maybe I am missing something here?

That confuses me? Why do arrays not work but generated functions do?

Here is a typical example:
28.4*(0.25-(y^2))*(0.25-(z^2))*(1.0-exp(-τ))
Inlet velocity depends on y and z coordinates and time. Arrays holding values at boundaries would take you only so far since you have to recompute them at ever time step anyhow. Such equations I would like to parse into Julia functions at the beginning of the simulation, compile them on the first time they are invoked, and used them as compiled functions for the rest of the simulation.

Why doesn’t

function(y, z, τ, arr)
     arr[1]*(arr[2]-(y^2))*(arr[3]-(z^2))*(arr[4]-exp(-τ))
end

work?

Because I am trying to make it as user input. The code is not just for me to run, and not just for one case to solve. Another example may involve sine, cosine functions, the functions change if the geometry moves and so forth.

The best user input is a function.

1 Like

The topic was also discussed here before.

2 Likes

Indeed. I was putting them in the wrong place. If I introduce them into the stag_compute, it works. It looks like this:

  function stag_compute(s :: Stag)                                               
    println(Base.invokelatest(s.b.value, 1.0, 1.0, 1.0))                                   
  end

But, as the Julia documentation writes: “The drawback is that invokelatest is somewhat slower than calling f directly, and the type of the result cannot be inferred by the compiler.” I could live with the “somewhat slower”, but don’t fancy the fact that " the type cannot be inferred by the compiler"