# My macro generates no code (?)

**URL:** https://discourse.julialang.org/t/my-macro-generates-no-code/133416
**Category:** General Usage
**Tags:** question, macros, metaprogramming
**Created:** [October 24, 2025, 12:05pm UTC](https://discourse.julialang.org/t/my-macro-generates-no-code/133416 "2025-10-24T12:05:27Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)
#### Post date: [October 24, 2025, 12:05pm UTC](https://discourse.julialang.org/t/my-macro-generates-no-code/133416/1 "2025-10-24T12:05:27Z")

</div>

I am quite stumped!

I have a macro, complicated yes, but I believe you can ignore most of its code, and still help me: If at the end of the macro definition, I `@show` the code it is to generate, it looks perfect. But if I invoque the macro, it injects zero code, so no function or variable created). If I launch `@macroexpand` on it: same, nothing to show. 😵‍💫

Any ideas? (I have an awkward feeling this will turn out to be a very silly question! 😃 ).  
Julia 1.11, Windows. I use `Revise.jl`, and yes I did restart Julia.

(Context, if you are curious: I am developing code that takes user-defined functions as input. If the user does this is a script, their input-function definition is reparsed at each script execution. Even if unchanged the input-function gets a new type, triggering recompilations. This code intends to work around this issue, while not imposing restrictions on type that would get in the way for forward automatic differentiation…)

```julia-auto
using Printf, MacroTools

muscadeerror = error

struct Functor{name,Ta} <: Function
    captured::Ta
    function Functor{name}(;kwargs...) where{name}
        nt = NamedTuple(kwargs)
        return new{name,typeof(nt)}(nt)
    end
end

# to debug, use 'Base.dump' on expressions
macro functor(capture,foo)
    # Build capargname, a vector of names of captured variables, to later replace a -> o.captured.a in the body of foo -> (o::Functor{:foo})
    if capture.head==:call # function call
        if length(capture.args)==1 # no captured arguments
            caparg = Any[]
            capargname = Symbol[]
            ncaparg = 0
        else
            if capture.args[2] isa Expr && capture.args[2].head isa Symbol && capture.args[2].head == :parameters # user not supposed to prefix captured args with ;, but I'm in a good mood
                error("Do not use ; in list of captured arguments")
            else
                caparg = capture.args[2:end]
            end
            ncaparg = length(caparg)
            capargname = Vector{Symbol}(undef,ncaparg)
            for icaparg = 1:ncaparg
                if caparg[icaparg] isa Symbol
                    capargname[icaparg] = caparg[icaparg]
                elseif caparg[icaparg] isa Expr
                    if caparg[icaparg].head == :kw
                        capargname[icaparg] = caparg[icaparg].args[1]
                    elseif caparg[icaparg].head == :parameters
                        muscadeerror("Invalid @functor definition 3")
                    end
                else
                    muscadeerror("Invalid @functor definition 4")
                end
            end
        end
    else
        muscadeerror("Invalid @functor definition 2")
    end

    # Build the code for the method associated to the functor 
    # TODO all variables must be either capturedargs or fooargs, no closure. Throw error otherwise
    foodict = splitdef(foo)
    foodict[:body] = MacroTools.postwalk(foodict[:body]) do ex
        ex isa Symbol && ex∈capargname ? :(o.captured.$ex) : ex # prefix captured args with `o.captured.` in method body
    end    
    fooname = foodict[:name] # :foo
    functortype = Expr(:curly,:Functor,QuoteNode(fooname))#:(Functor{$fooname}) # Functor{:foo}

    foodict[:name] = Expr(:(::),:o,functortype) # (o::Functor{:foo}), name of the method that implements foo(x)
    foo = combinedef(foodict) # code of said method
    quotefoo = MacroTools.postwalk(rmlines,foo)
    quotefoo = MacroTools.postwalk(unblock,quotefoo)
    quotefoo = QuoteNode(quotefoo) # quote of unannotated code for said method, to decide wether foo-code changed or not 

    # obscure variable name, to prevent reparsing of the foo definition
    tag = Symbol("tag_for_the_functor_macro_",fooname)

    # build the code for the call to the functor constructor
    caparg = Expr(:parameters,caparg...) # place a ; in front of the argument list (any prefixed ; was cleaned earlier)
    constrcall = Expr(:call,functortype,caparg) # Functor{:foo}(;a,b=2)
    constructfunctor = Expr(:(=),fooname,constrcall)
    
    code = quote 
        $constructfunctor
        if ~@isdefined($tag) || $tag ≠ $quotefoo
           $tag = $quotefoo
           $foo
        end                                                          
    end
    @show prettify(code) # looks perfect
    return code
end
####################
@macroexpand @functor with(a=2) cost7(x) = a*x # no output
@functor with(a=2) cost7(x) = a*x # no error
@show cost7(3) # error: what is cost7?

```

---

<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: [October 24, 2025, 12:22pm UTC](https://discourse.julialang.org/t/my-macro-generates-no-code/133416/2 "2025-10-24T12:22:24Z")

</div>

> [@Philippe\_Maincon1](#):
>
> ```julia-auto
> constructfunctor = Expr(:(=),fooname,constrcall)
> 
> ```

I believe you want `Expr(:(=), esc(fooname), constrcall)` here. If you want `fooname` to be available globally and not just inside the macro, you need to consider [macro hygiene](https://docs.julialang.org/en/v1/manual/metaprogramming/#Hygiene). That’s why, if you look at the output of `@macroexpand` it will show something like:

```julia-auto
quote
    #= REPL[14]:59 =#
    var"#46#cost7" = Main.Functor{:cost7}(; a = 2)
    #= REPL[14]:60 =#
    if ~($(Expr(:isdefined, Symbol("#47#tag_for_the_functor_macro_cost7")))) || var"#47#tag_for_the_functor_macro_cost7" ≠ $(QuoteNode(:(function (o::Functor{:cost7})(x; )
      o.captured.a * x
  end)))
        #= REPL[14]:61 =#
        var"#47#tag_for_the_functor_macro_cost7" = $(QuoteNode(:(function (o::Functor{:cost7})(x; )
      o.captured.a * x
  end)))
        #= REPL[14]:62 =#
        function (var"#48#o"::Main.Functor{:cost7})(var"#50#x"; )
            #= REPL[15]:2 =#
            (var"#48#o").captured.a * var"#50#x"
        end
    end
end

```

Note how it doesn’t say `cost7 = ...` in the first line, but instead `cost7` gets mangled in such a way that it doesn’t leak outside the macro.

---

<div class="post-metadata">

### Author: ![Philippe\_Maincon1](https://avatars.discourse-cdn.com/v4/letter/p/ec9cab/32.png) [@Philippe\_Maincon1](https://discourse.julialang.org/u/Philippe_Maincon1)
#### Post date: [October 24, 2025, 12:25pm UTC](https://discourse.julialang.org/t/my-macro-generates-no-code/133416/3 "2025-10-24T12:25:18Z")

</div>

Hi Simeon,

Great, I will fix that . thanks. But did you get output at all (e.g. from `@Macroexpand`). Again, I get nothing out. Not bad code, just _nothing_.

OK, so I had a combination of two problems: fooled by macro-hygiene, combined with something, that causes `@macroexpand` to return nothing on my computer, probably not on yours.

Well, this solves my problem, thank you, case closed.
