# Writing a macro that generates a function

**URL:** <https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315>\
**Category:** General Usage\
**Created:** [September 12, 2025, 9:10am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315 "2025-09-12T09:10:59Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 9:10am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/1 "2025-09-12T09:10:59Z")

</div>

I would like to do define a macro that allows me sum together vector-like structs, whose fields might be heterogeneous in terms of their types. I tried something like the following inside of a module:

```julia
"""
A heterogeneous vector. Actually the heterogeneity comes from the fields having different units from Unitful.jl, but this is a simplified example. I will have a few different kinds of types like these, and wish to reduce allocations that StaticArrays.FieldVector introduces due to the type heterogeneity.
"""
struct StateVector
    a :: Int
    b :: Float64
end

"""
This defines addition for FieldVectors.
"""
macro defineAdd(type)

    return quote

        T = $(esc(type))

        fields = fieldnames(T)

        sumExprs = [:(x.$f + y.$f) for f in fields]

        function Base.:+(x::T, y::T)

            T($(sumExprs...))

        end # function

    end # quote

end # macro

```

However, this does not work and I get the following error, where `M` is the module I am defining this in:

```julia
julia> M.@defineAdd M.StateVector
ERROR: LoadError: UndefVarError: `sumExprs` not defined in `M`
Suggestion: check for spelling errors or missing imports.
Stacktrace:
 [1] var"@defineAdd"( __source__ ::LineNumberNode, __module__ ::Module, type::Any)
   @ M ~/M.jl/src/M.jl:26
in expression starting at REPL[2]:1

```

How might I fix this error?

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 9:22am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/2 "2025-09-12T09:22:25Z")

</div>

I know I could just define an abstract supertype for these different vectors and call `propertynames` or `fieldnames` on those, but this causes allocations. I then need to generate things like

```julia
function Base.:+(a::StateVector,b::StateVector)

    StateVector(
        a.a + b.a,
        b.a + b.b,
    )

end # function

```

programmatically at compile-time.

---

<div class="post-metadata">

**Author:** ![gdalle](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gdalle/32/224680_2.png) [@gdalle](https://discourse.julialang.org/u/gdalle)\
**Post date:** [September 12, 2025, 9:24am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/3 "2025-09-12T09:24:00Z")

</div>

This sounds like a [generated function](https://docs.julialang.org/en/v1/manual/metaprogramming/#Generated-functions)?

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 9:27am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/4 "2025-09-12T09:27:09Z")

</div>

Sure. But I think my main problem now comes from the macro being defined in a module. All of the examples are always performed in the REPL, so I’m not sure how to proceed.

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 9:38am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/5 "2025-09-12T09:38:31Z")

</div>

I also wonder what would happen if I did “function piracy” like

```julia
@generated function Base.:+(a::T,b::T)
     ...
end # function
```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 12, 2025, 9:41am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/6 "2025-09-12T09:41:04Z")

</div>

Your immediate problem is that you try to interpolate something into a quote that was not defined before the quote but inside the quote.

Your main problem is that macros don’t have access to values when they are expanded, so they cannot query the fields of the type you give as argument.

This can be solved with a generated function, which has the critical distinction from normal macros in that it has access to type information.

Another approach that should be possible is to generate the add function together with the struct definition, with a macro

```julia-auto
@struct_with_add struct StateVector
    a :: Int
    b :: Float64
end

```

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 9:53am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/7 "2025-09-12T09:53:02Z")

</div>

Any examples of passing struct definitions to macros? I think the generated function approach can be a bit problematic, because essentially want to do [this](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/5), if I took that approach.

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 9:54am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/8 "2025-09-12T09:54:20Z")

</div>

Ah, I guess there is this to go on: [julia/base/util.jl at 9615af0f269df4d371b8010e9507ed5bae86103b · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/blob/9615af0f269df4d371b8010e9507ed5bae86103b/base/util.jl#L526-L562).

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 12, 2025, 9:58am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/9 "2025-09-12T09:58:15Z")

</div>

[GitHub - JuliaServices/AutoHashEquals.jl: A Julia macro to add == and hash() to composite types.](https://github.com/JuliaServices/AutoHashEquals.jl) is another classic example.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 12, 2025, 10:05am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/10 "2025-09-12T10:05:35Z")

</div>

> [@SeSodesa](#):
>
> I also wonder what would happen if I did “function piracy” like
> 
> ```julia-auto
> @generated function Base.:+(a::T,b::T)
> ...
> end # function
> 
> ```

If you can’t figure out a way to constrain the types in the generated function you can of course do the workaround of

```julia-auto
Base.:+(a::T, b::T) where {T <: MySuperType} = my_generated_function(a, b)

```

---

<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:** [September 12, 2025, 11:46am UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/11 "2025-09-12T11:46:23Z")

</div>

Would

```julia-auto
+(x, y) = StateVector(map(+, getfields(x), getfields(y))...)

```

work for you? Should be fast even without macros/generated functions.  
(`getfields` comes from ConstructionBase.jl; it also has `getproperties`)

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 1:06pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/12 "2025-09-12T13:06:09Z")

</div>

For the record, defining

```julia
"""
Summation for StateVectors.
"""
@generated function Base.:+(left::T,right::T) where T <: FieldVector

    propNames = fieldnames(T)

    return :(
        T(
            [:(left.$p + right.$p) for p in propNames]
        )
    )

end # function

```

results in an error

```julia
ERROR: The function body AST defined by this @generated function is not pure. This likely means it contains a closure, a comprehension or a generator.
Stacktrace:
 [1] top-level scope
   @ REPL[25]:1

```

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 1:17pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/13 "2025-09-12T13:17:15Z")

</div>

The following results in a different error:

```julia
"""
Summation for StateVectors.
"""
@generated function Base.:+(left::T,right::T) where T <: FieldVector

    @show fieldNames = fieldnames(T)

    @show fieldAdditionExprs = [:(left.$n + right.$n) for n in fieldNames]

    return :(
        T($fieldAdditionExprs...)
    )

end

```

```julia
ERROR: MethodError: no method matching StateVector(::Expr, ::Expr)

```

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 1:40pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/14 "2025-09-12T13:40:18Z")

</div>

So I guess now the question is, do I need to somehow define a constructor for `StateVector`, that takes two expressions and evaluates them?

```julia
function StateVector(e1::Expr,e2::Expr)

    StateVector(
        eval(e1),
        eval(e2),
    )

end # function

```

---

<div class="post-metadata">

**Author:** ![mcabbott](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mcabbott/32/6603_2.png) [@mcabbott](https://discourse.julialang.org/u/mcabbott)\
**Post date:** [September 12, 2025, 1:48pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/15 "2025-09-12T13:48:29Z")

</div>

I think you just missed a bracket, you want `T($(fieldAdditionExprs...))` splatting before interpolation, not `T($fieldAdditionExprs...)`.

```julia-auto
julia> struct StateVector
           a :: Int
           b :: Float64
       end

julia> @generated function Base.:+(left::T,right::T) where T <: StateVector

           @show fieldNames = fieldnames(T)

           @show fieldAdditionExprs = [:(left.$n + right.$n) for n in fieldNames]

           return :(
               T($(fieldAdditionExprs...))
           )

       end

julia> StateVector(1,2.0) + StateVector(3,4.0)
fieldNames = fieldnames(T) = (:a, :b)
fieldAdditionExprs = [$(Expr(:quote, :(left.:($(Expr(:$, :n))) + right.:($(Expr(:$, :n)))))) for n = fieldNames] = Expr[:(left.a + right.a), :(left.b + right.b)]
StateVector(4, 6.0)

```

Edit, but @aplavin 's solution is certainly easier to read:

```julia-auto
julia> using ConstructionBase

julia> Base.:*(x:: StateVector, y:: StateVector) = StateVector(map(*, getfields(x), getfields(y))...)

julia> StateVector(1,2.0) * StateVector(3,4.0)
StateVector(3, 8.0)

```

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 1:49pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/16 "2025-09-12T13:49:22Z")

</div>

And this was indeed it. Thanks so much!

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 1:50pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/17 "2025-09-12T13:50:51Z")

</div>

Now onto generating almost the entire AbstractVector interface like this (except for indexing)… 😃

---

<div class="post-metadata">

**Author:** ![SeSodesa](https://avatars.discourse-cdn.com/v4/letter/s/dc4da7/32.png) [@SeSodesa](https://discourse.julialang.org/u/SeSodesa)\
**Post date:** [September 12, 2025, 1:51pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/18 "2025-09-12T13:51:59Z")

</div>

I kind of prefer the lack of an additional dependency in this case. But `ConstructionBase` is good to know about.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [September 12, 2025, 1:54pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/19 "2025-09-12T13:54:19Z")

</div>

I don’t know the scope of your project but be prepared that you might gain runtime speed at the cost of compilation time, which may or may not be a favorable tradeoff.

---

<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:** [September 12, 2025, 4:53pm UTC](https://discourse.julialang.org/t/writing-a-macro-that-generates-a-function/132315/20 "2025-09-12T16:53:55Z")

</div>

Note that ConstructionBase.jl is very light and extremely widely used – it’s a dependency of thousands of Julia packages.

But I just checked, `getfields` implementation is just a few lines there:

```julia-auto
function getfields(obj::T) where {T}
    fnames = fieldnames(T)
    NamedTuple{fnames}(getfield.((obj,), fnames))
end

```

You can just copy-paste it.
