# Can I "unescape" a variable name within an \`esc(..)\` node?

**URL:** <https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396>\
**Category:** General Usage\
**Tags:** metaprogramming, macro-hygiene\
**Created:** [August 2, 2023, 1:15pm UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396 "2023-08-02T13:15:18Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![iago-lito](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iago-lito/32/31924_2.png) [@iago-lito](https://discourse.julialang.org/u/iago-lito)\
**Post date:** [August 2, 2023, 1:15pm UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396/1 "2023-08-02T13:15:18Z")

</div>

Hello, I’m curious whether it is possible to individually pick expressions within an [`esc()`](https://docs.julialang.org/en/v1/base/base/#Base.esc)aped node to make them hygienic again. For instance:

```julia
macro f(...)
    xp = esc(:(...))
    unescape!(xp.args[2]) # or something
    xp
end

```

This would be useful for using temporary variables within generated code that otherwise needs to be escaped. For instance:

```julia
module M

# The decorated structs will subtype this union.
abstract type A{T} end

# Not all T's are valid, and this function is responsible to check that.
is_correct_type(T) = true # (dummy)

macro decorate_struct(type_expression, str)
    code = quote end

    # Generate code to evaluate invoker's expression once
    # and either check validity or display friendly error.
    append!(code.args,
        (
            quote
                T = try # <- This variable be hygienically gensym'ed.
                    $type_expression
                catch e
                    error("Could not evaluate type expression: $($(repr(type_expression))): $e")
                end
                $is_correct_type(T) || error("Invalid type: $T")
            end
        ).args)

    # Use T to generate the actual struct definition code.
    str_name = str.args[2]
    str.args[2] = :($str_name <: $A{T}) # <- How to "unescape" this `T` here so it does refer to the gensym'ed version?

    push!(code.args, esc(str)) # <- This needs be escaped.

    code
end
export @decorate_struct

end

using .M
@decorate_struct Int64 struct First end # <- ERROR: `T` not defined.
@decorate_struct Int64 mutable struct Second
    a::First
    b::String
end

```

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [August 2, 2023, 7:06pm UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396/2 "2023-08-02T19:06:59Z")

</div>

No, such a function does not exist.

Looking over your code, I can’t help but notice an `eval`. Since you’re writing a macro, you shouldn’t really need to do that; the result of the macro expansion will already be evaluated (and the result of any `eval` in a local scope will only be visible once the code returns to a global scope).

---

<div class="post-metadata">

**Author:** ![iago-lito](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iago-lito/32/31924_2.png) [@iago-lito](https://discourse.julialang.org/u/iago-lito)\
**Post date:** [August 3, 2023, 1:19pm UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396/3 "2023-08-03T13:19:08Z")

</div>

Wops, you’re right, I’m removing it. Not sure how it ended up there 🙂 Thank you for your answer. Is there any obvious reason why such a `unescape!` function does/should not exist?

---

<div class="post-metadata">

**Author:** ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)\
**Post date:** [August 3, 2023, 3:13pm UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396/4 "2023-08-03T15:13:11Z")

</div>

It’s generally better to only escape the parts you definitely want to evaluate in the context where the macro was called; not everything the macro produces. Macro hygiene is not something you can just slap on an expression 🙂

---

<div class="post-metadata">

**Author:** ![iago-lito](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iago-lito/32/31924_2.png) [@iago-lito](https://discourse.julialang.org/u/iago-lito)\
**Post date:** [August 3, 2023, 6:22pm UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396/5 "2023-08-03T18:22:16Z")

</div>

> [@Sukera](#):
>
> It’s generally better to only escape the parts you definitely want to evaluate in the context where the macro was called

Well, I do totally agree with this. So maybe I can reformulate my problem better in these terms now 😉

Escaping only the parts I need would have been easy in the following situation, because it’s a small part to escape within a big unescaped, generated node:

```julia
macro generate_struct(name)
    :(struct esc($name) <: SuperType # <- The only part I need to escape within this unescaped generated :struct.
        a::Int64
        b::Float64
    end)
end

@generate_struct A

```

But it is _difficult_ when it happens to be the other way round: I need to ‘unescape’ a small part of a big, escaped, non-generated node:

```julia
macro decorate_struct(str)
    str_name = str.args[2]
    str.args[2] = :($str_name <: unescape(T)) # <- The only part I need *not* to escape..
    quote
        T = ...
        esc($str) # <- .. within this large escaped, *input* :struct.
    end
end

@generate_struct mutable struct A
    a::First
    b::String
end

```

How to best handle this situation then?

---

<div class="post-metadata">

**Author:** ![ffevotte](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ffevotte/32/6587_2.png) [@ffevotte](https://discourse.julialang.org/u/ffevotte)\
**Post date:** [August 3, 2023, 8:59pm UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396/6 "2023-08-03T20:59:33Z")

</div>

> [@iago-lito](#):
>
> This would be useful for using temporary variables within generated code that otherwise needs to be escaped.

One thing you can do is using [`gensym`](https://docs.julialang.org/en/v1/base/base/#Base.gensym) yourself to generate an hygienic name for the temporary variable.

If I understand correctly what you want to do, a simplified version could look like this:

```julia
julia> module M
           abstract type A{T} end
       
           macro decorate_struct(str)
               str_name = str.args[2]
               Tsym = gensym(:T) # Tsym will be escaped but it's OK because it can't clash with user-provided code
               str.args[2] = :($str_name <: $A{$Tsym})
               quote
                   $(esc(Tsym)) = Int
                   $(esc(str))
               end
           end
       end
Main.M

julia> @macroexpand M.@decorate_struct struct Foo end
quote
    #= REPL[1]:9 =#
    var"##T#292" = Main.M.Int
    #= REPL[1]:10 =#
    struct Foo <: (Main.M.A){var"##T#292"}
        #= REPL[2]:1 =#
    end
end

```

which seems to work as intended:

```julia
julia> M.@decorate_struct struct Foo end
julia> supertype(Foo)
Main.M.A{Int64}

```

---

<div class="post-metadata">

**Author:** ![iago-lito](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/iago-lito/32/31924_2.png) [@iago-lito](https://discourse.julialang.org/u/iago-lito)\
**Post date:** [August 4, 2023, 9:41am UTC](https://discourse.julialang.org/t/can-i-unescape-a-variable-name-within-an-esc-node/102396/7 "2023-08-04T09:41:48Z")

</div>

Oh, I like this approach @ffevotte 🙂

From the doc of [gensym](https://docs.julialang.org/en/v1/base/base/#Base.gensym), I understand that the generated name is only hygienic _“within the same module”_. IIUC the `gensym(:T)` in your snippet is executed within the macro _definition_ module `M`, so it is guaranteed to not clash with any variable name in `M`… but is it also guaranteed to not clash within the macro _invocation_ module?

If not, should I enforce it with something akin to `Tsym = Core.eval( __module__ , :(gensym(:T)))` instead? Or maybe I misunderstand how `gensym` works and this is not necessary?
