# Macro, loops and variables

**URL:** <https://discourse.julialang.org/t/macro-loops-and-variables/78669>\
**Category:** General Usage\
**Tags:** question, macros\
**Created:** [March 29, 2022, 11:04am UTC](https://discourse.julialang.org/t/macro-loops-and-variables/78669 "2022-03-29T11:04:41Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![FedeClaudi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fedeclaudi/32/33490_2.png) [@FedeClaudi](https://discourse.julialang.org/u/FedeClaudi)\
**Post date:** [March 29, 2022, 11:04am UTC](https://discourse.julialang.org/t/macro-loops-and-variables/78669/1 "2022-03-29T11:04:41Z")

</div>

Hi everyone,

I have a macro question. It’s probably something about variables [hygene](https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene) as mentioned in this other [topic](https://discourse.julialang.org/t/macro-cannot-see-variables-in-calling-functions-scope/34806), but I can’t quite figure out, any help would be appreciated

I’m trying to do a macro that wraps around a for loop and does the following:

1. adds some code before and inside the loop, specifically it creates and updates a [Term.jl](https://github.com/FedeClaudi/Term.jl) progressbar.
2. it puts the loop inside a `try/finally` statement to correctly stop the progressbar if something goes wrong (otherwise this would cause issues).

I’ve got most of it to work, but if the body of the for loop uses a variable that is defined in the local scope where the loop is done, the macro can’t see it (hence why I think I should put an `esc` somewhere, but don’t know where).

This is what I’ve got:

```julia
macro track(args...)
    quote
        # get pbar number of iterations
        iterobj = $args[1].args[1].args[2]
        N = length(eval(iterobj))

        # get loop expressions
        loop_header = $args[1].args[1]
        loop_body = $args[1].args[2]

        # do loop in try expression to ensure pbar stops
        pbar = nothing # to have access to it in the try scope
        try
            # start progressbar
            pbar = ProgressBar()
            start!(pbar)
            __pbarjob = addjob!(pbar; N=N)

            # create new expression for loop
            complete = Expr(
                $args[1].head,
                loop_header, 
                Expr(
                    loop_body.head,
                    loop_body.args...,
                    :(sleep(.01)),
                    :(update!($__pbarjob))
                )
            )
            
            # evaluate new expression
            eval(complete)
        finally
            stop!(pbar)
        end
    end
end

```

`complete` is an `Expr` that includes the original loop with an additional line in the body: ` :(update!($__pbarjob))` to update the progressbar.

Then if I do something like this:

```julia
function test()
    @track for i in 1:10
        sleep(0.005)
    end
end

test()

```

it works and I get a progress bar update at each iteration of the loop:  
 ![image](https://global.discourse-cdn.com/julialang/original/3X/5/0/501651514bd140da5dd9410273b1a4d478410dae.png)

However, if I change `test` to:

```julia
function test()
    x = 0
    @track for i in 1:10
        x += 1
        sleep(0.005)
    end
    println(x)
end

```

I get:

```julia
ERROR: UndefVarError: x not defined
Stacktrace:
 [1] top-level scope
   @ ~/Documents/Github/Term.jl/temp2.jl:48 [inlined]
 [2] top-level scope
   @ ./none:0

```

Any idea what I should change to make this work?

---

<div class="post-metadata">

**Author:** ![N5N3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/n5n3/32/17663_2.png) [@N5N3](https://discourse.julialang.org/u/N5N3)\
**Post date:** [March 29, 2022, 11:36am UTC](https://discourse.julialang.org/t/macro-loops-and-variables/78669/2 "2022-03-29T11:36:55Z")

</div>

I guess this is what you want

```julia
import Term.progress: track, ProgressBar, update, start, stop
macro track(ex)
    iter = esc(ex.args[1].args[2])
    i = esc(ex.args[1].args[1])
    body = esc(ex.args[2])
    quote
        N = length($iter)
        let pbar = ProgressBar(;N=N)
            try 
                for $i in $iter
                    $body
                    update(pbar)
                end
            finally
                stop(pbar)
            end
        end
        nothing
    end
end

```

---

<div class="post-metadata">

**Author:** ![FedeClaudi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fedeclaudi/32/33490_2.png) [@FedeClaudi](https://discourse.julialang.org/u/FedeClaudi)\
**Post date:** [March 29, 2022, 11:53am UTC](https://discourse.julialang.org/t/macro-loops-and-variables/78669/3 "2022-03-29T11:53:49Z")

</div>

That’s exactly what I needed and so much better than what I was converging upon. I’m still a bit confused about where stuff should happen inside the body of a macro I guess.

The progressbar syntax is a bit different because I’m working on a re-write of progressbars in Term, but this is what it looks like with the progressbar logic inside a try/catch statement:

```julia
macro track(ex)
    iter = esc(ex.args[1].args[2])
    i = esc(ex.args[1].args[1])
    body = esc(ex.args[2])
    quote
        pbar = nothing # needed to access it within the try/catch
        try
            pbar = ProgressBar()
            start!(pbar)
            __pbarjob = addjob!(pbar; N=length($iter))
            for $i in $iter
                update!(__pbarjob) 
                $body
            end
            update!(__pbarjob)
            render(pbar)
        finally 
            stop!(pbar)
        end
        nothing
    end
end

```

Thank you!

---

<div class="post-metadata">

**Author:** ![yha](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yha/32/3502_2.png) [@yha](https://discourse.julialang.org/u/yha)\
**Post date:** [March 29, 2022, 2:44pm UTC](https://discourse.julialang.org/t/macro-loops-and-variables/78669/4 "2022-03-29T14:44:04Z")

</div>

[ProgressLogging.jl](https://github.com/JuliaLogging/ProgressLogging.jl) already has a `@progress` macro, that supports fancier stuff like multi-variable for loops and comprehensions.  
Wouldn’t it make more sense to plug into their API to add support for `Term.jl` progress bars (the way `TerminalLoggers.jl` works)?

---

<div class="post-metadata">

**Author:** ![FedeClaudi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/fedeclaudi/32/33490_2.png) [@FedeClaudi](https://discourse.julialang.org/u/FedeClaudi)\
**Post date:** [March 29, 2022, 4:49pm UTC](https://discourse.julialang.org/t/macro-loops-and-variables/78669/5 "2022-03-29T16:49:17Z")

</div>

Thanks for suggesting it. I’ve recently had a [long discussion](https://github.com/FedeClaudi/Term.jl/discussions/67) about that. It will happen but it might be worth for Term to have its own interface too.
