Evaluating expressions in file

Hi,
I’m hitting one of these eval() scope-related issue, that I don’t know how to solve… maybe you can help me. This is related to an old Julia 0.6.2 code of mine, which was reading initial data from an external file, which contans lines like the following

x  = 1.0
Nv = 10

which are initial values for some variables. Now inside my code I read the file, put every line in a string

record[1] = "x  = 1.0"
record[2] = "Nv = 10"

and was evaluating that inside a computation function, with eval(parse(redord[1]), eval(parse(redord[2]) and so on and so forth for each record.
Now I’ve realized that since Julia 1.0 this is no longer possible, as eval works only in the global scope, and not inside a function. At this point, my code does not work anymore… So what would be a way to properly code this?
I mean, I have to read the file and evaluate each line it contains inside my function, but the story is that not every file contains the same variable initialization, other file my have lines like Nh = 50 and not refer to x or Nv above, so I do not know what variables should I initialize beforehand…

Thanks,

Ferran

Any help? please? I need to read files containing assignations to variables, something I could easily do before the scope rules changed… Now that all these have changed, I can’t manage to read them.
I can’t change the input files either, as these come from a different project :frowning:

Hi!
I think the answer depends on the context.
Some ideas that come to my mind:

Just put everything in global scope:

x = 0
y = 0
z = 0
eval(Meta.parse("x = 1.0"))
f(x, y, z)

For that you need to have all the variables you can possibly need initialized first, otherwise the function call will fail. If there is nothing else in that file you can probably just do include(file).
If that’s too much clutter you can put the same thing inside a struct and update the values you read in.

Other option: create a bespoke function:

record = "x = 1"
expr = quote
    function f()
        # here you paste the records:
        $record
        # here the calculation
        return x * 2
    end
end
eval(ex)
f()

I think if you can expand on what you exactly want to do it’s easier to recommend something specific.

Just treat the file as julia code and include it to initialise variables with those names?

Thanks for the replies :slight_smile:

Still the situation is a little bit more involved that including the file. The story is that this format files are inherited from old fortran, where there is a (very useful!) instruction called NAMELIST. In there you have a large file containing different records, one for each run, and separated by some END_OF_RECORD string. One such (simplified) file would be something like the following

o-o-o-o-o-o-o-o-o-o

Nv = 5
Nh = 10

o-o-o-o-o-o-o-o-o-o

Nv = 10
dens = 0.021

o-o-o-o-o-o-o-o-o-o

Nh = 15
L = 0.016

o-o-o-o-o-o-o-o-o-o

Here we have three records, one the program must run three times, one for each record. At each run, only variables appearing in the record are updated, the other ones keep the same value they had in the preceding record.
I can not include the whole file at once, as only the last repeated values would be taken into account. I could split the file in three different ones, and run the code for each one, but then the calues in the previous records would not be inherited. Of course I could update the records will all the previous values, but that can be amazing for large input files.

That was easily handled with the eval(parse()) thing in previous julia versions, but not anymore. So the question is whether this functionality could be kept or recovered, now that eval() does not work on a local scope…

Thanks again,

Ferran.

Well, I suggested two (or three, depending how you want to count) versions that do what you want. After the more detailed explanation I wouldn’t advise the second version but the first version (both that in the global scope as well as that with the struct) should work fine. What do you think about those?

Edit: What I mean with “the struct version”:

mutable struct A
    x::Int
    y::Int
    z::Int
    A() = new(0, 0, 0)
end
a = A()
record = "x = 1"
n = split(record)[1]
v = parse(Int, split(record)[end])
setproperty!(a, Symbol(n), v)
f(a)
1 Like

but that is what is was done originally, everything in glibal scope. But you need to loop over all lines in a record to eval() them, and that no longer works as the scope inside the loop is not yhe global one…
That’s what it was working in Julia 0.6.2, but not anymore

I think I don’t quite understand why this is a problem. You can just have all your variables in the global scope, and the updated will be evaled there too, no matter where the eval is called from?

To nitpick eval has always only worked in the global scope. The difference is that global variables no longer are automatically in scope inside a function. If you know which variables are used in your computations you can just declare all of them global at the start of the function and the rest should work as in 0.6.

1 Like

This is completely outside the intended use of the package, but you could try if SoftGlobalScope works for your specific problem.

Julia 0.6:

julia> function foo()
           eval(parse("x = 13"))
           return x
       end
foo (generic function with 1 method)

julia> foo()
13

julia> x
13

Julia 1.2:

julia> using SoftGlobalScope

julia> @softscope function foo()
           eval(Meta.parse("x = 13"))
           return x
       end
foo (generic function with 1 method)

julia> foo()
13

julia> x
13

Oh, I see different options that will habe to try… Thank you guys a lot for providing so much help! I’ll report back once I can try them :slight_smile:

Thanks GunnarFarneback, but when decorating a variable with the global token, I have to give them a value, and that will override the value it had in the previous execution, while I only want that to happen in the set of variables I need to update, not in all of them…

Really?

julia> x, y = 1, 2
(1, 2)

julia> function foo()
           global x, y
           eval(Meta.parse("x = 13"))
           return x + y
       end
foo (generic function with 1 method)

julia> foo()
15

Of course, using global variables in the first place is not a particularly good approach (rather the opposite) but just reviving the functionality from 0.6 shouldn’t be that hard.

1 Like

Sorry, I think I start to be too tired for Julia coding… I said that because I tried

sss = 13
function oly()
    global sss
end
oly()

and got

syntax: misplaced "global" declaration

Stacktrace:
 [1] top-level scope at /home/mazzanti/.julia/packages/IJulia/F1GUo/src/kernel.jl:52

but then after your comment, just tried

sss = 13
function oly()
    global sss
    println(sss)
end
oly()

and got the expected result

13

…so now I see this can be a solution. Sorry for being that moron :frowning:

That’s not a great error message but the reason is that you have global in the last statement, making it equivalent to

function oly()
    return global sss
end

and apparently you can’t have global in a return statement, which I suppose makes sense.

Edit: Right, should have tried with an assignment too. I think this is slightly fishy and may be worth filing an issue about.

Hi @Ferran_Mazzanti, did you figure out a solution to your problem? If you did, please post it in case others with similar problems see it (like myself :slight_smile:).

And regarding your main program – are you doing some kind of attribution analysis, changing one variable at a time to see the incremental impact on some output variable? I couldn’t quite follow your explanation in a previous post.