Include a script from external file in different scopes

question

#1

Hello everyone,
I am currently working on a script in which the same function “g” has to be evaluated multiple times, also within other functions.

1. a=3

2. function g(y)
3.   a*y
4. end

5. function f(x,y)
6.   function g(y)
7.     a*y
8.   end
9.   a=4
10.   x+g(y)
11. end

12. println(g(3))
13. println(f(1,3))

This function “g” depends on both the argument “y” passed to the function itself and a certain parameter “a”. The script above does exactly what I want, indeed, the command println(g(3)) returns value 9, evaluated adopting parameter a=3 defined in the Main module, while println(f(1,3)) returns 13, meaning that when “g” is called within function “f”, this is evaluated with the value of a=4 defined in the scope of function “f”.

In the script on which I am working, “g” appears many times and every time I want it to be evaluated with the parameter “a” defined in the scope of the caller, without passing “a” as an argument to the function.

Since “g” has always the same form I would like to import it from an external file “g.jl” in order to make modifications to the form of “g” easier to implement. The script may appear as follows:

file g.jl:

1. function g(y)
2.   a*y
3. end

file myscript.jl:

1. a=3

2. include("g.jl")

3. function f(x,y)
4.   include("g.jl")
5.   a=4
6.   x+g(y)
7. end

8. println(g(3))
9. println(f(1,3))

However now, the command println(f(1,3)) returns 10 and not 13, meaning that when “g” is called, that is evaluated adopting a=3 from the main. So my question is: is there any way of including a script present in an external file executing it only in the scope from which it is called? Hope my question is clear. Thanks to anybody willing to help.


#2

Include always works at the global scope, just like eval. You can use a macro:

macro insert_g()
  quote
    function g(y)
      a*y
    end
  end
end

...
function f(x,y)
   @insert_g
    ...
end

#3

Thanks a lot mauro3! The suggestion of using macro is great, but I could not run the code you posted. I have modified it as follows and it works perfectly:

1. a=3

2. macro include_g()
3.   return :(function g(y) a*y end)
4. end

5. @include_g

6. function f(x,y)
7.   @include_g
8.   a=4
9.   x+g(y)
10. end

11. println(g(3))
12. println(f(1,3))

Your help has been fundamental, many thanks!


#4

Note that this macro definition is wrong. g must be escaped.


#5

Here’s a macro which generates macros like that:

Use with caution. (The fixed version includes the escaping @yuyichao mentions).


#6

Very interesting suggestions, many thanks to everyone! I apologise because I am quite new to Julia, but could someone explain to me why does the code I wrote works fine even without escaping the function.

Or, in other words, what is the risk of using the code I propose?

1. a=3

2. macro include_g()
3.   return :(function g(y) a*y end)
4. end

5. @include_g

6. function f(x,y)
7.   @include_g
8.   a=4
9.   x+g(y)
10. end

11. println(g(3))
12. println(f(1,3))

many thanks!


#7

Because you are relying on a bug in < 0.6.


#8

A better way to do this is just to write your “script” as a function that takes g as an argument:

function mystuff(g, ...other args...)
     ... do stuff with g(x) ...
end

Then, define your actual function as

function g(x, a)
     return x * a # or whatever
end

and call mystuff for different a values by constructing anonymous functions:

mystuff(x -> g(x, a1), ...)
mystuff(x -> g(x, a2), ...)

and so on.

In general, if you find yourself messing around with macros and code generation just to call a function with different parameters, you are probably making a mistake.

Note also that using global variables like a=3 kills performance (and is problematic from a software-engineering standpoint as well).