Soft vs Hard Scopes, Macros, and `@everywhere`

I’m reading the section on the manual regarding the scope of variables:

https://docs.julialang.org/en/v1.5/manual/variables-and-scoping/

It says that macros introduce a hard local scope. There is some ambiguity here which confused me a little since there are two ways in which macros enter your code: the first is when you define them, and the second is when you call them.

I assume this table is referring to the definition of macros. However, I am then confused by the following behaviour, which is why I went to the manual:

julia> module Foo
       using Distributed
       @everywhere x = 1
       f() = print(x)
       end
Main.Foo

julia> Foo.f()
ERROR: UndefVarError: x not defined
Stacktrace:
 [1] f() at ./REPL[1]:4
 [2] top-level scope at REPL[2]:1

On the other hand, if you don’t wrap the code in a module:

julia> using Distributed

julia> @everywhere x = 1

julia> f() = print(x)
f (generic function with 1 method)

julia> f()
1

This made me think that calling macros introduced a soft local scope. (Though I may have misunderstood the manual here; I’m not convinced I fully understand the distinction yet.) However, this seems special to the @everywhere macro.

julia> module Bar
       @eval x = 1
       f() = print(x)
       end
Main.Bar

julia> Bar.f()
1

I’m not really sure what’s going on, and the documentation for @everywhere and @eval don’t reveal too much about this.

The behaviour is exactly the same if you put these commands into a .jl file and run them using include.

Can anyone help explain what’s happening?

I don’t think its really related to scope. The docs for @everywhere actually do give a hint, they say the expression is evaluated under Main, so even when @everywhere x = 1 is inside Foo, it sets a variable x in the Main module.

Depending where you actually want the variable, you could @everywhere @eval Foo x = 1 to instead set it inside Foo (note you need to define the module on the workers too, so put another @everywhere around the module definition), or you could print(Main.x) instead.

3 Likes

Aha. And here I thought I read that part carefully.

In a way, it is related to scope: just not local vs global, but it’s about which global scope.

Thanks for the clarification!

Aha. And here I thought I read that part carefully.

In a way, it is related to scope: just not local vs global, but it’s about which global scope.

Thanks for the clarification!