# Using @which inside a macro on expr

**URL:** https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372
**Category:** General Usage
**Tags:** metaprogramming
**Created:** [December 7, 2022, 3:43pm UTC](https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372 "2022-12-07T15:43:02Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)
#### Post date: [December 7, 2022, 3:43pm UTC](https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372/1 "2022-12-07T15:43:02Z")

</div>

What is the best way to call a macro with an `Expr` and then get the evaluated output of the macro? I want to write a macro that needs to call @which on its input and then use the returned method. Here is my current attempt

```julia
macro whichfile(ex...)
    m = @which ex... # doesn't work, because `ex...` is not the original code, but an expr
    file, line = functionloc(m)
    println(file)
    nothing
end

```

after reading [Is there a trick to input an `Expr` into a macro? - #4 by johnmyleswhite](https://discourse.julialang.org/t/is-there-a-trick-to-input-an-expr-into-a-macro/82383/4) I tried the following:

```julia
macro whichfile(ex...)
    m = var"@which"(LineNumberNode(@ __LINE__ (), @ __FILE__ ()), @ __MODULE__ , ex...)
    # does not work, because m is now the code outpout of @which and not the method
    file, line = functionloc(m)
    println(file)
    nothing
end

```

and also

```julia
macro whichfile(ex...)
    m = var"@which"(LineNumberNode(@ __LINE__ (), @ __FILE__ ()), @ __MODULE__ , ex...)
    file, line = functionloc(eval(m)) # complains about invalid syntax (escape (outerref println))
    println(file)
    nothing
end

@whichfile println("hello world")

```

---

<div class="post-metadata">

### Author: ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)
#### Post date: [December 7, 2022, 3:55pm UTC](https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372/2 "2022-12-07T15:55:00Z")

</div>

Okay, I found a solution with `macroexpand` myself. And because other people might like hit, here the complete macro:

```julia
julia> macro splitedit(ex...)
           m = eval(macroexpand(Main, :(@which $(ex...))))
           file, line = functionloc(m)
           editor = ENV["EDITOR"]
           editorcmd = "$editor +$line $file"
           splitcmd = `tmux split-window "$editorcmd"`
           run(splitcmd)
           nothing
       end
@splitedit (macro with 1 method)

julia> @splitedit println("hallo") # same as @edit, but in a new tmux pane

```

---

<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: [December 7, 2022, 7:20pm UTC](https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372/3 "2022-12-07T19:20:11Z")

</div>

While this might work in an interactive context, but a macro should not evaluate code. Instead, it should rewrite the expression it gets passed.  
Check for example, what `@which` or `@edit` are doing:

```julia
julia> @macroexpand @which println("hallo")
:(InteractiveUtils.which(println, (Base.typesof)("hallo")))

julia> @macroexpand @edit println("hallo")
:(InteractiveUtils.edit(println, (Base.typesof)("hallo")))

```

Thus, they just call a corresponding function with parts of the expression wrapped in `typesof`.

In your case, a similar setup would work:

```julia
function splitedit(fun, argtypes)
    m = which(fun, argtypes)
    file, line = functionloc(m)
    editor = ENV["EDITOR"]
    editorcmd = "$editor +$line $file"
    splitcmd = `tmux split-window "$editorcmd"`
    run(splitcmd)
    nothing
end

```

and then have your macro expand into a call of that function, i.e., `@macroexpand @splitedit println("hallo")` should give the expression `:(splitedit(println, Base.typesof("hallo")))`.

---

<div class="post-metadata">

### Author: ![jlbosse](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jlbosse/32/11274_2.png) [@jlbosse](https://discourse.julialang.org/u/jlbosse)
#### Post date: [December 8, 2022, 12:28pm UTC](https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372/4 "2022-12-08T12:28:05Z")

</div>

Good point, while I couldn’t fully get around calling eval, the following version is hopefully a bit better, and also something very neat to have in the `startup.jl` file

```julia
"""
    splitedit(file, line=0)

Open `file` at `line` with your favorite editor in a new tmux pane.
The editor can be changed by setting the `EDITOR` environment variable.
"""
function splitedit(file, line=0)
    editor = ENV["EDITOR"]
    editorcmd = "$editor +$line $file"
    splitcmd = `tmux split-window "$editorcmd"`
    run(splitcmd)
    nothing
end

"""
    @splitedit <functioncall>

Evaluates the arguments to the function call, determines their types,
and calls the `splitedit` function on the resulting expression.
"""
macro splitedit(ex)
    fun = eval(ex.args[1])
    argtypes = Base.typesof(ex.args[2:end])
    m = which(fun, argtypes)
    file, line = functionloc(m)
    splitedit(file, line)
end

```

---

<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: [December 8, 2022, 10:56pm UTC](https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372/5 "2022-12-08T22:56:51Z")

</div>

This is not what I meant, i.e., you have now just separated part of your code into a separate function. Your macro still evaluates the expression it gets passed and does all its processing at compile time. Keep in mind that a macro is just a function which happens to be executed on a parsed expression by the compiler. In turn, its return value – usually another expression – will then be compiled instead of the original expression and eventually evaluated just like regular code. Note that your macro returns nothing, i.e., all its work is done at compile time!  
Let’s have a look at a slightly adapted example from the Julia documentation on [Metaprogramming](https://docs.julialang.org/en/v1/manual/metaprogramming/)

```julia
julia> macro twostep(arg)
           println("I execute at parse time. The argument is: ", arg, " with type ", typeof(arg))
           return :(println("I execute at runtime. The argument is: ", $arg, " with type ", typeof($arg)))
       end
@twostep (macro with 1 method)

# Note: Expanding the macro runs the first println and returns an expression
julia> ex = @macroexpand @twostep(1 + 2);
I execute at parse time. The argument is: 1 + 2 with type Expr

# The returned expression contains another call of println with the args expression inserted
julia> ex
:(Main.println("I execute at runtime. The argument is: ", 1 + 2, " with type ", Main.typeof(1 + 2)))

# Evaluating it actually executes it
julia> eval(ex)
I execute at runtime. The argument is: 3 with type Int64

# Calling the macro does both steps, i.e., first expanding it and then evaluating the expression it returned
julia> @twostep(1 + 2);
I execute at parse time. The argument is: 1 + 2 with type Expr
I execute at runtime. The argument is: 3 with type Int64

```

Now in your case, I was proposing the following:

```julia
# Function repeated from above
function splitedit(fun, argtypes)
    m = which(fun, argtypes)
    file, line = functionloc(m)
    editor = ENV["EDITOR"]
    editorcmd = "$editor +$line $file"
    splitcmd = `tmux split-window "$editorcmd"`
    run(splitcmd)
    nothing
end

macro splitedit(expr)
    @assert expr.head == :call # We only support simple call expressions
    return :(splitedit($(esc(expr.args[1])), tuple($(map(arg -> :(typeof($(esc(arg)))), expr.args[2:end])...))))
end

```

Note that this macro does not evaluate `expr`, instead it constructs a new expression which will perform the desired computation/function call when evaluated:

```julia
julia> @macroexpand @splitedit println("hallo")
:(Main.splitedit(println, Main.tuple(Main.typeof("hallo"))))

```

---

<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: [December 9, 2022, 10:59pm UTC](https://discourse.julialang.org/t/using-which-inside-a-macro-on-expr/91372/6 "2022-12-09T22:59:02Z")

</div>

Just a small add on why a macro should not try to evaluate the expression that it gets passed:

1. `eval` evaluates an expression in the global scope, i.e., cannot see local variables:

2. An expression contains symbols for variables and not their values

In contrast, my macro expands into a function call and is the same as if you had written

```julia
let fun = println, z = 3
    splitedit(fun, tuple(typeof(z), typeof(2))) # expansion of @splitedit fun(z, 2)
end

```

and does not run into the above issues. In general, think of macros as a shorter notation for some longer boilerplate code that you could write yourself. The task of the macro is then to rewrite the desired shorter expression into this intended longer boilerplate expression, i.e., it takes an unevaluated expression as input and produces another unevaluated expression as output.
