# How to return a macro from a macro?

**URL:** <https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612>\
**Category:** General Usage\
**Tags:** question, macros\
**Created:** [September 22, 2026, 4:46pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612 "2026-09-22T16:46:15Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![ysaak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ysaak/32/224272_2.png) [@ysaak](https://discourse.julialang.org/u/ysaak)\
**Post date:** [September 22, 2026, 4:46pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/1 "2026-09-22T16:46:15Z")

</div>

I want to create a macro that receives the header `foo(args)` and body `begin ... end` and returns a macro version of it.

```julia-auto
macro macwo(header::Expr, body::Expr)
    #=
    macro header
        body
    end
    =#
end

# Implementation example
@macwo foo(a::Integer, b, c) begin
    println(a + b + c)
end

@foo(1,2,3)

```

But I’m new to Julia and I can’t do it by reading Julia’s documentation.

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [September 22, 2026, 7:32pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/2 "2026-09-22T19:32:47Z")

</div>

Hi, and welcome to the Julia community!

* * *

I’m not sure I completely understand what you want to achieve (i.e. why this is easier than just writing

```julia
macro foo(a::Integer, b, c)
    println(a + b + c)
end

```

directly, or just creating a `function`), so perhaps a more complicated example would be useful. But for this case I guess

```julia
macro macwo(header::Expr, body::Expr)
    esc(quote
        macro $(header.args[1])($(header.args[2:end]...))
            $body
        end
    end)
end

```

works:

```julia-repl
julia> @macwo foo(a::Integer, b, c) begin
    println(a + b + c)
end
@foo (macro with 1 method)

julia> @foo(1, 2, 3)
6

```

The idea here is that we want to output essentially the same macro (expression) as I typed at the start of my post

```julia-repl
julia> using MacroTools

julia> @macroexpand @macwo foo(a::Integer, b, c) begin
           println(a + b + c)
       end
quote
    #= REPL[1]:3 =#
    macro foo(a::Integer, b, c)
        #= REPL[1]:3 =#
        #= REPL[1]:4 =#
        begin
            #= REPL[5]:2 =#
            println(a + b + c)
        end
    end
end

```

(Without the [escaping](https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene) `esc` we would have `Main.foo`, `Main.Integer` and `Main.println`.) Now, to write our macro `macwo` it is useful to take a look at the input we are getting:

```julia-repl
julia> macro show_args(header, body)
           dump(header)
           dump(body)
       end
@show_args (macro with 1 method)

julia> @show_args foo(a::Integer, b, c) begin
           println(a + b + c)
       end
Expr
  head: Symbol call
  args: Array{Any}((4,))
    1: Symbol foo
    2: Expr
      head: Symbol ::
      args: Array{Any}((2,))
        1: Symbol a
        2: Symbol Integer
    3: Symbol b
    4: Symbol c
Expr
  head: Symbol block
  args: Array{Any}((2,))
    1: LineNumberNode
      line: Int64 2
      file: Symbol REPL[11]
    2: Expr
      head: Symbol call
      args: Array{Any}((2,))
        1: Symbol println
        2: Expr
          head: Symbol call
          args: Array{Any}((4,))
            1: Symbol +
            2: Symbol a
            3: Symbol b
            4: Symbol c

```

In this way we can see that our method name `foo` can be found as `header.args[1]`, while the argument list is present in `header.args[2:end]`, which we [splat (and interpolate)](https://docs.julialang.org/en/v1/manual/metaprogramming/#Splatting-interpolation) to get three arguments instead of a single length-3 array.

You could also add additional checks, depending on how robust you want your macro to be. For example, we expect `header.head === :call`. Currently,

```julia-auto
@macwo foo[a::Integer, b, c] begin
     println(a + b + c)
end

```

would behave identically, even though here `header.head === :ref`.

```julia-repl
julia> macro macwo(header::Expr, body::Expr)
           @assert header.head === :call
           esc(quote
               macro $(header.args[1])($(header.args[2:end]...))
                   $body
               end
           end)
       end
@macwo (macro with 1 method)

julia> @macwo foo(a::Integer, b, c) begin
           println(a + b + c)
       end
@foo (macro with 1 method)

julia> @macwo foo[a::Integer, b, c] begin
           println(a + b + c)
       end
ERROR: LoadError: AssertionError: header.head === :call
(...)

```

* * *

Edit: An alternative to inspecting the expression tree, is to use `MacroTools.jl`’s `@capture`, which can be easier to read:

```julia
macro macwo(header::Expr, body::Expr)
    @capture(header, f_(a__))
    esc(quote
        macro $f($(a...))
            $body
        end
    end)
end

```

* * *

By the way, I see that the automatic translation thinks you wrote the original message in Japanese, and that it ‘translated’ “receives” to “takes”. This might be a relevant issue for [Testing automatic translations to English](https://discourse.julialang.org/t/testing-automatic-translations-to-english/139522).

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 22, 2026, 7:45pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/3 "2026-09-22T19:45:29Z")

</div>

> [@eldee](#):
>
> By the way, I see that the automatic translation thinks you wrote the original message in Japanese

I’m not seeing any indication that this post was automatically translated, maybe it’s a UI problem on my end. How are you seeing it?

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [September 22, 2026, 7:50pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/4 "2026-09-22T19:50:20Z")

</div>

In the top-right corner there used to be the translation icon and message on hover. But after refreshing the page now, it has also disappeared for me. Additionally the message has gone back to using “receives”. So I guess it already fixed itself… 🙂

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [September 22, 2026, 9:14pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/5 "2026-09-22T21:14:14Z")

</div>

I think it’s more involved than the macro proposed by @eldee. At least one needs to remove the type declarations:

```julia-auto
julia> x = 1; @foo(x, 2, 3)
ERROR: LoadError: MethodError: no method matching var"@foo"(::LineNumberNode, ::Module, ::Symbol, ::Int64, ::Int64)
The function `@foo` exists, but no method is defined for this combination of argument types.

Closest candidates are:
  var"@foo"(::LineNumberNode, ::Module, ::Integer, ::Any, ::Any)
   @ Main REPL[1]:3

```

---

<div class="post-metadata">

**Author:** ![eldee](https://avatars.discourse-cdn.com/v4/letter/e/b5a626/32.png) [@eldee](https://discourse.julialang.org/u/eldee)\
**Post date:** [September 22, 2026, 10:06pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/6 "2026-09-22T22:06:11Z")

</div>

Apart from the type, the main issue is simply that the macro `foo` performs its print at parse time, when we do not know the value of `x`. Note that we do not have

```julia-auto
macro foo2(a, b, c)
    esc(:(println($a + $b + $c)))
end

```

for which

```julia-repl
julia> x = 1;

julia> @foo2(x, 2, 3)
6

```

does work. Also note the difference between

```julia-repl
julia> for _ = 1:5
           @foo(1, 2, 3)
       end
6

julia> for _ = 1:5
           @foo2(1, 2, 3)
       end
6
6
6
6
6

```

But from @ysaak’s description it’s not clear to me which of these behaviours (if any) is desired.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [September 23, 2026, 12:23pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/7 "2026-09-23T12:23:47Z")

</div>

> [@ysaak](#):
>
> But I’m new to Julia and I can’t do it by reading Julia’s documentation.

Generically I wouldn’t recommend worrying about macros when you are first starting out. They can be a very useful tool, but only in a small minority of cases, and are easy to misunderstand. See also the discussion in [How to warn new users away from metaprogramming](https://discourse.julialang.org/t/how-to-warn-new-users-away-from-metaprogramming/35022)

---

<div class="post-metadata">

**Author:** ![ysaak](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ysaak/32/224272_2.png) [@ysaak](https://discourse.julialang.org/u/ysaak)\
**Post date:** [September 23, 2026, 3:35pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/8 "2026-09-23T15:35:31Z")

</div>

Thank you guys for the support!  
My goal with macros in Julia is to make easier to work with some common macros.  
Because of this, I had a big interest on how to handle with the AST from Julia.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [September 23, 2026, 9:08pm UTC](https://discourse.julialang.org/t/how-to-return-a-macro-from-a-macro/139612/9 "2026-09-23T21:08:29Z")

</div>

Could you provide a small specific example of those common macros and what you intend to do? We appreciate the effort to make a minimal general example, but that opened up broader questions you probably didn’t intend to be discussed and couldn’t really be answered to a practical extent.
