# Replace variable name in expression

**URL:** <https://discourse.julialang.org/t/replace-variable-name-in-expression/93186>\
**Category:** General Usage\
**Created:** [January 19, 2023, 12:08am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186 "2023-01-19T00:08:34Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 19, 2023, 12:08am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/1 "2023-01-19T00:08:34Z")

</div>

I have a Julia expression that references the variable `x`, and need to replace all `x` references to `y`. However, I don’t find a reasonable way to do this.

`MacroTools` is promising:

```julia
julia> MacroTools.replace(:( x + 1 ), :x, :y)
:(y + 1)

```

but still wrong:

```julia
julia> MacroTools.replace(:( f(x, (x=1,)) ), :x, :y)
:(f(y, (y = 1,)))
# should be :(f(y, (x = 1,)))

```

Any suggestions?

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 19, 2023, 12:16am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/2 "2023-01-19T00:16:02Z")

</div>

`MacroTools.replace` looks like an uncodumented internal. The right way to do this is with [MacroTools.postwalk](http://fluxml.ai/MacroTools.jl/stable/pattern-matching/#Expression-Walking-1).

```julia
julia> using MacroTools

julia> MacroTools.postwalk(:( f(x, (x=1,)) )) do ex
           ex == :x ? :y : ex
       end
:(f(y, (y = 1,)))

```

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 19, 2023, 12:24am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/3 "2023-01-19T00:24:15Z")

</div>

The result is the same: it replaces occurrences of the `x` symbol even when they are unrelated to the `x` variable.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 19, 2023, 12:30am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/4 "2023-01-19T00:30:23Z")

</div>

Oh, I misread the code you had posted, and confused the # should be with the result.

Can you explain what you mean here by “unrelated to the x variable”? Expressions don’t label objects by scope, they’re just symbols, the x on it’s own and the x in (x = 1,) are on the exact same footing as far as the Expr is concerned.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 19, 2023, 12:34am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/5 "2023-01-19T00:34:47Z")

</div>

When running this code, Julia distinguishes between the two `x`s - so it’s possible to distinguish them. Clearly, only the first `x` in `f(x, (x=1,))` is related to the `x` variable, and the second `x` is just a name of a field.

In a macro, I want to take an input expression depending on `x`, like the body in `f(x) = ... body ...`, and turn it into an equivalent expression that depends on `y`. Only variable references need to change, not field names.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 19, 2023, 12:51am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/6 "2023-01-19T00:51:59Z")

</div>

Okay, so it sounds like you want the scopes of symbols in the expression to be resolved assuming the expression follows regular julia semantics.

> When running this code, Julia distinguishes between the two `x`s - so it’s possible to distinguish them.

Sure, but that happens during lowering which is after macroexpansion. It’s not a property of the expression but a property of the IR. Unfortunately, Julia does not really expose it’s own scoping resolution to us in a form that we can use.

I’ve run into the same need as you before, and to my knowledge so far, the only real option is to use [JuliaVariables.jl](https://github.com/JuliaStaging/JuliaVariables.jl) for this. Unfortunately, JuliaVariables.jl is a buggy, incomplete subset of the actual full rules, so this can’t really be relied upon for general code, but depending on your use-case it might be enough.

```julia
julia> using JuliaVariables

julia> let ex = :( f(x, (x=1,)))
           ex |> simplify_ex |> solve_from_local!
       end
:($(Expr(:scoped, (bounds = Var[], freevars = Var[], bound_inits = Symbol[]), :((@global f)(@global x, (x = 1,))))))

```

What’s important here is that now the variables are labelled by their scope. So `f` is known to come from outside the expression, and one of the `x`s is known to come from outside but another is local.

You could then do a `postwalk` over this ‘solved’ expression and only replace the `@global x` instead of regular `x`. But I’d also seriously consider another path because this package is _not_ fully reliable.

---

<div class="post-metadata">

**Author:** ![mbaz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbaz/32/17295_2.png) [@mbaz](https://discourse.julialang.org/u/mbaz)\
**Post date:** [January 19, 2023, 1:11am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/7 "2023-01-19T01:11:09Z")

</div>

> [@Mason](#):
>
> `:(f(y, (y = 1,)))`

Here’s one way to do it with MacroTools. The obvious problem is that it only works for this particular case; generalizing this approach is probably too painful.

```julia
julia> MacroTools.postwalk(:(f(x, (x=1,)))) do ex
                if @capture(ex, f_(x_, (a_ = b_,)))
                    x === :x ? :($f(:y, ($a=$b,))) : ex
                else
                    return ex
                end
              end
:(f(:y, (x = 1,)))

```

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 19, 2023, 1:53am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/8 "2023-01-19T01:53:06Z")

</div>

Sure, that will work for the case shown in the OP, but I (maybe wrongly?) suspected that @aplavin’s actual usecase is significantly more complicated than that

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 19, 2023, 9:42am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/9 "2023-01-19T09:42:11Z")

</div>

Wow, didn’t expect this to be that deep of an issue!

Indeed, I want such replacements for arbitrary input expression, not for some specific forms. Moreover, I would argue that’s a common need for many transformation macros. Typically, this issue is totally ignored by macro writers just because there is no clear solution.

Is it feasible to expose variable resolution functionality from Julia directly as some function in Base?

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [January 20, 2023, 8:27pm UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/10 "2023-01-20T20:27:40Z")

</div>

I think such a thing might not be very straightforward because as far as I’m aware, the scope resolution is not an isolated process. It’s kinda just mixed into the process for how julia lowers things to IR.

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [January 22, 2023, 11:08pm UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/11 "2023-01-22T23:08:46Z")

</div>

This is probably a deep issue in any language supporting macros. E.g., in Common Lisp such a functionality is known as a _code walker_ and apparently it is not possible to write a fully portable code walker in ANSI Common Lisp (see for example [Macroexpand-All: an example of a simple lisp code walker](https://dl.acm.org/doi/abs/10.1145/173770.173773) for some of the difficulties).  
To illustrate the issue, let’s make a small macro that replaces every `x` by `y` (just for the sake of argument and including the same problem that you mentioned above):

```julia
macro xy(expr)
     MacroTools.postwalk(expr) do ex
         ex == :x ? :y : ex
     end
end

```

Now, try to use it in a context involving another macro (again this macro is just for the sake of argument and in general, macros should not introduce unbound identifiers)

```julia
macro inc_x()
    x = esc(:x)
    :($x + 1)
end

```

and it will fail miserably

```julia
julia> @xy let x = 1; 2 * x end
2

julia> @xy let x = 1; @inc_x() end
ERROR: UndefVarError: x not defined

```

Thus, to properly replace every free occurrence of an identifier it is at least required to expand all macros within its body – as otherwise it is impossible to know in which context the identifier will appear. This requires substantial knowledge about the language, i.e., handling of environments, scoping rules etc. I’m not aware of any language were such rules are fully exposed to the user.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [January 23, 2023, 12:39am UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/12 "2023-01-23T00:39:12Z")

</div>

My question was about expression that don’t contain macros: either they didn’t in the first place, or were already expanded. Then, it’s possible to distinguish variables vs all other symbols without any extra information: it’s clearly possible by looking at the code in complete isolation.

> [@bertschi](#):
>
> see for example [Macroexpand-All: an example of a simple lisp code walker](https://dl.acm.org/doi/abs/10.1145/173770.173773) for some of the difficulties

In Julia, we have `@macroexpand` that expands all macros. Is it what the article talks about?

---

<div class="post-metadata">

**Author:** ![bertschi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/bertschi/32/33462_2.png) [@bertschi](https://discourse.julialang.org/u/bertschi)\
**Post date:** [January 23, 2023, 7:10pm UTC](https://discourse.julialang.org/t/replace-variable-name-in-expression/93186/13 "2023-01-23T19:10:52Z")

</div>

You still need a code walker (macroexpand-all was just an example and yes, macroexpansion is easier in Julia than Common Lisp, i.e., there are no local or symbol macros) which is aware of all special forms, e.g., let, function, if, while, named tuples etc., in order to prevent replacing (or expanding) symbols in certain contexts. I’m not aware of any such tool, let alone a full documentation of all special forms, i.e., syntactic constructs with special rules for lowering or evaluation.
