# Using @sprintf inside a macro

**URL:** <https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275>\
**Category:** General Usage\
**Tags:** macros\
**Created:** [August 20, 2020, 4:37pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275 "2020-08-20T16:37:02Z")\
**Posts on this page:** 12\
**Page:** 2

<div class="post-metadata">

**Author:** ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)\
**Post date:** [August 24, 2020, 7:06pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/22 "2020-08-24T19:06:31Z")

</div>

This is explained in the docs:

[https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene-1](https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene-1)

> Julia’s macro expander solves these problems in the following way. First, variables within a macro result are classified as either local or global. A variable is considered local if it is assigned to (and not declared global), declared local, or used as a function argument name. Otherwise, it is considered global. Local variables are then renamed to be unique (using the [`gensym`](https://docs.julialang.org/en/v1/base/base/#Base.gensym)function, which generates new symbols), and global variables are resolved within the macro definition environment. Therefore both of the above concerns are handled; the macro’s locals will not conflict with any user variables, and `time` and `println` will refer to the Julia Base definitions.

Since those variables are not assigned to they are not considered local to the macro and do not have gensymed variables created for them. Thus they do not need to be escaped.

Edit: Leaving out the escape will cause behavior that is likely to be unexpected if the user feeds code into the macro that includes an assignment operation and user expects that assignment operation to apply to a variable outside of the macro.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [August 24, 2020, 7:07pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/23 "2020-08-24T19:07:30Z")

</div>

Indeed, my code doesn’t use esc, but it does splat the y into a tuple and then splat the tuple into the call… and this works… I’m not sure why it works but just splatting y into the call doesn’t

---

<div class="post-metadata">

**Author:** ![anj](https://avatars.discourse-cdn.com/v4/letter/a/779978/32.png) [@anj](https://discourse.julialang.org/u/anj)\
**Post date:** [August 25, 2020, 10:26am UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/24 "2020-08-25T10:26:09Z")

</div>

@dlakelan, your code fails for me with exact same error (unknown r)  
i.e. does this work for you (julia 1.5)?

```julia
import Printf.@sprintf; 
import Dates.now;
macro myprintf(x,y...)
       :(print(now(),":",@sprintf($x,($(y...),)...)))
end

function t()
    r = 21
    @myprintf("failing %.f\n", r * 2)
end

#calling function t is failing with unknown r
t()

```

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [August 25, 2020, 11:11am UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/25 "2020-08-25T11:11:33Z")

</div>

Which version of Julia are you on? Are you in a fresh session? I can’t reproduce this on 1.5.

---

<div class="post-metadata">

**Author:** ![anj](https://avatars.discourse-cdn.com/v4/letter/a/779978/32.png) [@anj](https://discourse.julialang.org/u/anj)\
**Post date:** [August 25, 2020, 4:05pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/26 "2020-08-25T16:05:08Z")

</div>

yes. 1.5, fresh

```julia
               _
   _ _ _(_)_ | Documentation: https://docs.julialang.org
  (_) | (_) (_) |
   _ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` | |
  | | |_| | | | (_| | | Version 1.5.0 (2020-08-01)
 _/ |\ __'_|_|_|\__'_| | Official https://julialang.org/ release
|__/ |

julia> import Printf.@sprintf;

julia> import Dates.now;

julia> macro myprintf(x,y...)
              :(print(now(),":",@sprintf($x,($(y...),)...)))
       end
@myprintf (macro with 1 method)

julia>

julia> function t()
           r = 21
           @myprintf("failing %.f\n", r * 2)
       end
t (generic function with 1 method)

julia>

julia> #calling function t is failing with unknown r

julia> t()
ERROR: UndefVarError: r not defined
Stacktrace:
 [1] macro expansion at D:\buildbot\worker\package_win64\build\usr\share\julia\stdlib\v1.5\Printf\src\Printf.jl:1230 [inlined]
 [2] t() at .\REPL[4]:3
 [3] top-level scope at REPL[6]:1

```

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [August 25, 2020, 4:07pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/27 "2020-08-25T16:07:42Z")

</div>

Yes, that won’t work, because it doesn’t use `esc`. I meant that one:

> [@anj](#):
>
> ```julia
> import Printf.@sprintf; 
> import Dates.now;
> macro myprintf(x, y...)
> :(print(now(), ":", @sprintf($x, $(esc.(y)...))))
> end
> 
> function t()
> r = 21
> @myprintf("failing %.f\n", r * 2)
> end
> 
> #calling function t is failing with unknown r
> t()
> 
> ```

Does this one not work for you?

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [August 27, 2020, 6:11am UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/28 "2020-08-27T06:11:11Z")

</div>

Doesn’t esc mean that the symbol y will be used instead of the value of the macro argument y. I’m confused

---

<div class="post-metadata">

**Author:** ![anj](https://avatars.discourse-cdn.com/v4/letter/a/779978/32.png) [@anj](https://discourse.julialang.org/u/anj)\
**Post date:** [August 27, 2020, 10:03am UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/29 "2020-08-27T10:03:07Z")

</div>

yes, sorry about that. My fault. Now it all works. Thank you very much  
Fingers crossed this covers all the scenarios now.

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [August 27, 2020, 10:50am UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/30 "2020-08-27T10:50:36Z")

</div>

No, by default, all symbols in macros will be resolved in the scope where the macro is defined and all variables a macro assigns to get a randomized name as to not leak any definitions into the scope the macro is called in. If you actually want to reference variables in the scope the macro is called from or want to be able to define new variables in the macro call’s current scope, you need to wrap the symbol in `esc`. This leaves these symbols alone during macro expansion. You can also wrap whole expressions in `esc`, but you need to be careful, because you don’t want to accidentally leak variables, or reference functions from the caller’s scope, when you actually want to call a function defined in the scope the macro is defined in. This can lead to subtle bugs, which is why it’s not the default. You might want to read the section in the docs about [macro hygiene](https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene) to learn more.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [August 27, 2020, 1:33pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/31 "2020-08-27T13:33:09Z")

</div>

Yes, I have read the macro hygiene section. I guess it didn’t help me much.

what I understand is that if you define a variable in your macro, it will have it’s name changed to not collide with existing variables in the macro eval context.

what I don’t understand is what esc does. it seems it should prevent the macro system from changing the names of variables. You’re broadcasting the esc across y. so it means you insert the contents of y without any macro hygiene changes?

---

<div class="post-metadata">

**Author:** ![simeonschaub](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simeonschaub/32/216566_2.png) [@simeonschaub](https://discourse.julialang.org/u/simeonschaub)\
**Post date:** [August 27, 2020, 1:53pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/32 "2020-08-27T13:53:29Z")

</div>

It produces a special type of expression, namely, `esc(:x)` produces `Expr(:escape, :x)`. Whenever macro expansion then encounters such an expression, it knows not to apply macro hygiene here. The exact expression type is probably an implementation detail, all you really need to know is that it’s some kind of marker specifically for macro expansion.

---

<div class="post-metadata">

**Author:** ![dlakelan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlakelan/32/8491_2.png) [@dlakelan](https://discourse.julialang.org/u/dlakelan)\
**Post date:** [August 27, 2020, 3:11pm UTC](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275/33 "2020-08-27T15:11:47Z")

</div>

It would really help me to understand where things go wrong if we could step through this a bit. Suppose you have the following macro:

```julia
macro mysprintf(x,y)
   quote
      print(now(),":",@sprintf($x,$y))
    end
end

```

This seems to work fine for me… and it makes perfect sense. Why all the other fuss?

I guess the issue is we can only provide one argument… So then we do:

```julia
macro mysprintf(x,y...)
       quote print(now(),":",@sprintf($x,$(y)...))
       end
end

```

And when we call it like:

```julia
julia> @mysprintf("%d,%s",x*2,:y)
ERROR: MethodError: no method matching isfinite(::Expr)
Closest candidates are:
  isfinite(::BigFloat) at mpfr.jl:896
  isfinite(::Missing) at missing.jl:100
  isfinite(::Float16) at float.jl:555
  ...
Stacktrace:
 [1] top-level scope at REPL[42]:2

```

What does this error really mean?

How/why does doing

```julia
macro myprintf(x, y...)
    :(print(now(), ":", @sprintf($x, $(esc.(y)...))))
end

```

fix the problem?

[Previous page](https://discourse.julialang.org/t/using-sprintf-inside-a-macro/45275.md?page=1)
