# Should macros get an \`\_\_istoplevel\_\_\` or \`\_\_scope\_\_\` argument?

**URL:** https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850
**Category:** Internals & Design
**Created:** [August 13, 2020, 3:39am UTC](https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850 "2020-08-13T03:39:50Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [August 13, 2020, 3:39am UTC](https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850/1 "2020-08-13T03:39:50Z")

</div>

Because the same code will behave differently in global vs local scope, if one wants to write a macro which can work in both, in theory it needs to know into which scope its being expanded, right?

I’m running into this trying to a solve a problem with [UnPack](https://github.com/mauro3/UnPack.jl/pull/11). I noticed that `@unpack` accidently leaks variables into toplevel scopes, because it roughtly expands e.g. `@unpack x = foo()` to

```julia
varXXX = foo()
x = varXXX.x
varXXX

```

(The reason for doing this is to return the entire contents of `foo()`, rather than just the unpacked variables, consistent with Julia tuple unpacking)

This is fine in a local scope, but in a global scope it leaves all these gensym’ed `varXXX` variables around whose memory never gets GC’ed.

To fix this, you’d want to expand instead into the following in a global scope (but keep the original thing in a local scope, since the following would not work there correctly):

```julia
# when expanding into a global scope:
let varXXX = foo()
    global x = varXXX.x
    varXXX
end

```

Since macro’s currently don’t know which scope they’re in, you can’t do it. As far as I can tell, no other workaround is currently possible in Julia at all, so its impossible to write an `@unpack` macro which works in both scopes. If the macro could branch on something like `if __istoplevel__ ` or something, then it would be straightforward though.

(FWIW, another place this would be useful is in my [Memoization.jl](https://github.com/marius311/Memoization.jl/blob/c24c6eebce50fecb45896534cc5c7ec2b8ef0253/src/Memoization.jl#L93-L101) package where caches need to be cleared on redefinition only in the global scope, although there, unlike here, there does exist a [workaround](https://discourse.julialang.org/t/is-there-a-way-to-determine-whether-code-is-toplevel/39939/2), but which is pretty ugly)

Any thoughts on if something like this could be added? Or other potential workarounds?

---

<div class="post-metadata">

### Author: ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)
#### Post date: [August 13, 2020, 4:36am UTC](https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850/2 "2020-08-13T04:36:05Z")

</div>

You can simple declare `varXXX` as local.

Scope is determined after macro expansion so this is generally impossible to know at macro expansion time.

---

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [August 13, 2020, 4:40am UTC](https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850/3 "2020-08-13T04:40:06Z")

</div>

> [@yuyichao](#):
>
> You can simple declare `varXXX` as local.

Do you mean,

```julia
local x
let varXXX = foo()
    x = varXXX.x
    varXXX
end

```

This doesn’t work in the global scope.

> [@yuyichao](#):
>
> Scope is determined after macro expansion so this is generally impossible to know at macro expansion time.

Interesting didn’t know that thanks. That sounds like that will make this hard…

---

<div class="post-metadata">

### Author: ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)
#### Post date: [August 13, 2020, 5:10am UTC](https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850/4 "2020-08-13T05:10:15Z")

</div>

No, I said make `varXXX` local, not `x`. i.e just add `local` to `varXXX`.

---

<div class="post-metadata">

### Author: ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)
#### Post date: [August 13, 2020, 6:31am UTC](https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850/5 "2020-08-13T06:31:07Z")

</div>

Oh sorry, I read that wrong. Yea, that can be used to fix UnPack, thanks!

I hadn’t thought of that mainly because I hadn’t realized Julia has the following behavior:

```julia
julia> local x = 1
1

julia> x
ERROR: UndefVarError: x not defined

```

You learn something new about Julia scope every day 😄

I would still find a ` __istoplevel__ ` macro argument useful fwiw, but sounds like its impossible without some structural changes, and maybe there’s less motivation for it now that I see this.

---

<div class="post-metadata">

### Author: ![yuyichao](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yuyichao/32/20_2.png) [@yuyichao](https://discourse.julialang.org/u/yuyichao)
#### Post date: [August 13, 2020, 7:05am UTC](https://discourse.julialang.org/t/should-macros-get-an-istoplevel-or-scope-argument/44850/7 "2020-08-13T07:05:23Z")

</div>

This feature (allowing local in begin in global scope) pretty much exists for this exact purpose.
