# Eval scoping in macros, or removing eval completely

**URL:** <https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602>\
**Category:** General Usage\
**Tags:** metaprogramming\
**Created:** [February 4, 2021, 9:48am UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602 "2021-02-04T09:48:53Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Libbum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/libbum/32/3935_2.png) [@Libbum](https://discourse.julialang.org/u/Libbum)\
**Post date:** [February 4, 2021, 9:48am UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/1 "2021-02-04T09:48:53Z")

</div>

Hi all,

Took a stroll into marco coding and got totally lost. Trying to solve [this issue](https://github.com/JuliaDynamics/Agents.jl/issues/409).

Breakdown:  
We have a simple enough macro helper `@agent`, which generates a few different types of struct depending on input.

```julia
@agent Zombie OSMAgent begin
  infected::Bool
end
# --->
mutable struct Zombie <: AbstractAgent
      id::Int64
      pos::Tuple{Int64,Int64,Float64}
      route::Array{Int64,1}
      destination::Tuple{Int64,Int64,Float64}
      infected::Bool
end

```

The current implementation looks like:

```julia
macro agent(name, base, fields)
    base_type = Core.eval(@ __MODULE__ , base)
    base_fieldnames = fieldnames(base_type)
    base_types = [t for t in base_type.types]
    base_fields = [:($f::$T) for (f, T) in zip(base_fieldnames, base_types)]
    res = :(mutable struct $(esc(name)) <: AbstractAgent end)
    push!(res.args[end].args, base_fields...)
    push!(res.args[end].args, map(esc,fields.args)...)
    return res
end

```

I want to get around using the `eval` call, since it’s breaking incremental compilation if this macro is called within a module (see the issue raised above). This also happens if I use the global eval rather than the module scoped one.

Is there a way I can get the `fieldnames` and `.types` from my `base` variable/expression without having to call `eval`? If not, is there a way to satisfy the compiler in this instance?

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [February 4, 2021, 10:45am UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/2 "2021-02-04T10:45:35Z")

</div>

You could create the `OSMAgent` type with a macro, which at the same time creates a `@agent_OSMAgent` macro which can then contain all the info it needs as it is constructed with `OSMAgent`. I use something similar in Parameters.jl, in which the `@with_kw` macro constructs a type devinition and also a macro `@unpack_ThatType` which unpacks all fields into variables: [Parameters manual · Parameters.jl](https://mauro3.github.io/Parameters.jl/stable/manual/#The-type-specific-(un)pack-all-macros-(somewhat-dangerous)). Unfortunately, the code of Parameters.jl is a bit messy, so not sure whether you could actually use it, but you can try.

---

<div class="post-metadata">

**Author:** ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)\
**Post date:** [February 4, 2021, 8:57pm UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/3 "2021-02-04T20:57:26Z")

</div>

You can use `getfield` on the module to look up a symbol in a module. If the representation of the type is unqualified and non-parametric this is all you need.

An example of a qualified representation is `Base.RefValue`. Since it is qualified you will need to run getfield on the qualifiers recursively until you get to the type itself. In this case you would need to use `getfield` to extract the field by `:Base` in the current module and then check the `:RefValue` within it. This is required as `getfield` only accepts symbols as arguments and the path to the type is an expression.

---

<div class="post-metadata">

**Author:** ![Libbum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/libbum/32/3935_2.png) [@Libbum](https://discourse.julialang.org/u/Libbum)\
**Post date:** [February 4, 2021, 10:21pm UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/4 "2021-02-04T22:21:22Z")

</div>

Thanks @WschW, that’s got me on the right track. I’m happy to just support unqualified types, but will need to cover a specific parametric type.

[GridAgent{Int}](https://github.com/JuliaDynamics/Agents.jl/blob/70bd52d8a2b49c1eb63f3bafc29046317e80b7d4/src/core/agents.jl#L86) and [ContinuousAgent{Int}](https://github.com/JuliaDynamics/Agents.jl/blob/70bd52d8a2b49c1eb63f3bafc29046317e80b7d4/src/core/agents.jl#L98). I’ll take a look at that myself soon, but if you know a nice way to build the type signatures, that’d be really helpful.

Current (not working) progress:

```julia
    if hasfield(typeof(base), :args)    
        # This option assumes only one parametric    
        # type for dimensionality. Will need to be    
        # modified for more complex types.    
        base_type = getfield(@ __MODULE__ , base.args[1])    
        base_types = collect(base_type.body.types) #<-- These are not currently appropriate. Need to incorporate args[2]
    else                                                                                                                               
        base_type = getfield(@ __MODULE__ , base)                                                                                        
        base_types = collect(base_type.types)                                                                                          
    end                                                                                                                                
    base_fieldnames = fieldnames(base_type)

```

---

<div class="post-metadata">

**Author:** ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)\
**Post date:** [February 4, 2021, 10:53pm UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/5 "2021-02-04T22:53:58Z")

</div>

That’s a nice approach. I’m kind of interested in this, having wanted something similar myself, mainly a way to splice fields from one struct into another struct, as a kind of inheritance. Similar to what [Classes.jl](https://github.com/rjplevin/Classes.jl) does, but without some of the other stuff, and without needing the “superclass” to be defined in any special way. Anyway, sorry to slightly hijack this thread, but here’s a MWE using your idea:

```julia
julia> using MacroTools

julia> macro extends(structdef)
           fields = []
           for f in structdef.args[3].args
               isexpr(f, :(...)) ? append!(fields, fieldnames(getfield( __module__ , f.args[1]))) : push!(fields, f)
           end
           structdef.args[3] = :(begin $(fields...) end)
           structdef
       end
@extends (macro with 1 method)

julia> struct Foo
           x
           y
       end

# I think this would be a nice straightforward syntax for it:
julia> @extends struct Bar
           Foo...
           z
       end

julia> Bar |> fieldnames
(:x, :y, :z)

```

Its also straightforward how to make parametric types work too as long as the types were explicit, like

```julia
@extends struct Foo
    Complex{Float64}...
    other
end

```

would be easy since all the info we need is here:

```julia
julia> fieldnames(Complex{Float64})
(:re, :im)

julia> fieldtypes(Complex{Float64})
(Float64, Float64)

```

I’m curious whether there’s a way to make it work with type arguments too, like could you do this?:

```julia
@extends struct Foo{T}
    Complex{T}...
    other
end

```

The necessary info must be somewhere but I can’t find a Julia function that exposes it. Maybe with a C API call?

---

<div class="post-metadata">

**Author:** ![WschW](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/wschw/32/6575_2.png) [@WschW](https://discourse.julialang.org/u/WschW)\
**Post date:** [February 5, 2021, 12:50am UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/6 "2021-02-05T00:50:08Z")

</div>

If you want to support qualified types something like this function:

> <https://github.com/WschW/StructuralInheritance.jl/blob/ac7c8b560a710c12767c4f9dc654ca7679af38e9/src/StructuralInheritance.jl#L158>

can be used to split the qualified type into an array of the qualifying symbols and the name of the type which can help in looking up the type.

Side note: This package uses `eval` for the same thing you have been using `eval` for and while I have been meaning to get rid of it and a few other things I never got around to it as no one including myself uses it.

Edit: Also for this kind of use case you may want to look into this package:

> **[GitHub - rafaqz/Mixers.jl: Julia mixin macros. Mixed, not stirred](https://github.com/rafaqz/Mixers.jl)**
>
> Julia mixin macros. Mixed, not stirred. Contribute to rafaqz/Mixers.jl development by creating an account on GitHub.

---

<div class="post-metadata">

**Author:** ![Libbum](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/libbum/32/3935_2.png) [@Libbum](https://discourse.julialang.org/u/Libbum)\
**Post date:** [February 5, 2021, 10:54am UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/7 "2021-02-05T10:54:18Z")

</div>

Thanks all, these are really helpful! I’ll dig a little deeper and report back once I have something workable.

---

<div class="post-metadata">

**Author:** ![marius311](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/marius311/32/3953_2.png) [@marius311](https://discourse.julialang.org/u/marius311)\
**Post date:** [February 5, 2021, 11:42am UTC](https://discourse.julialang.org/t/eval-scoping-in-macros-or-removing-eval-completely/54602/8 "2021-02-05T11:42:46Z")

</div>

Ok, think I got it working. No C API needed, it always surprises me what you can do in pure Julia. I called it [StructExtender.jl](https://github.com/marius311/StructExtender.jl) for now, example from its readme:

```julia
struct Foo{X,Y}
    x :: X
    y :: Y
end

@extends struct Bar{X,Y,Z}
    Foo{X,Y}...
    z :: Z
end

# equivalent to defining:
struct Bar{X,Y,Z}
    x :: X
    y :: Y
    z :: Z
end

```

Feel free to play with it, I think it could be used to solve @Libbum’s original problem. Will probably sit on it for a bit then consider registering. Curious if any of you guys have ideas for the name of the package and macro, maybe there’s something better / clearer.
