# Metaprogramming: Obtain actual type from Symbol for field inheritance

**URL:** <https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912>\
**Category:** General Usage\
**Tags:** macros, metaprogramming, inheritance\
**Created:** [July 28, 2022, 10:38am UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912 "2022-07-28T10:38:19Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 28, 2022, 10:38am UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/1 "2022-07-28T10:38:19Z")

</div>

Hello,

I admit, having read [How to get the type of a symbol in a macro?](https://discourse.julialang.org/t/how-to-get-the-type-of-a-symbol-in-a-macro/12016) I realize that this may be harder than I thought, but perhaps if I describe what I want to achieve, a solution can be made for this specific case.

In Agents.jl we have a macro, that comes directly from this answer: [Composition and inheritance: the Julian way - #5 by dfdx](https://discourse.julialang.org/t/composition-and-inheritance-the-julian-way/11231/5) that allows us to create “agents” (mutable structs) that have some fields coming from some other structs. Like so:

```julia
# The following is within the `Agents` module:
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

```

and having e.g., also defined sometjhing like

```julia
# this is also within `Agents` module
mutable struct GraphAgent
    id::Int
    pos::Int
end

```

A user can do

```julia
using Agents
@agent MyAgent GraphAgent begin
    name::String
end

```

which would generate a mutable struct `MyAgent` with the additional `name` field that would also have the fields `id, pos`.

Now, because of the usage of `@ __MODULE__ `, this macro can only be used to “inherit” types that are defined within `Agents`. We want to extend this to allow using types defined outside `Agents`. My thinking is that this would be simple to do if the user also provides the module the types were defined at. **My problem is, I do not know how to use multiple dispatch in a macro** to actually avchieve that. My idea is to allow the second macro version:

```julia

@agent MySecondAgent MyAgent MyModule begin
    age::Int
end

```

that now would allow using any type `MyAgent` defined in module `MyModule` (which will typically be `Main`), and “inhereting” the fields of that type.

I’ve tried to achieve this with several different versions, but I always mess up in ways I don’t understand… I can post here my failed code versions if it helps 😃

---

<div class="post-metadata">

**Author:** ![cscherrer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/cscherrer/32/7631_2.png) [@cscherrer](https://discourse.julialang.org/u/cscherrer)\
**Post date:** [July 28, 2022, 12:57pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/2 "2022-07-28T12:57:43Z")

</div>

Is it that you need the module where the macro is expanded? Any macro has ` __source__ ` and ` __module__ ` defined implicitly, so you can always just use these variables when it’s helpful. To see the origin of a type, you can also use `parentmodule`. Your use case might have other subtleties, but I’d guess there might be a way to avoid having the user specify this.

As for multiple dispatch… Macros take the AST, nothing more. So what it knows is just what Julia knows after parsing: most things are `Expr`s. The macro itself can’t dispatch on the type an `Expr` eventually resolves to. A solution is to pass the values you want to dispatch on to a helper function of some sort, and have that take care of dispatch.

Here’s a toy example:

```julia
macro foo(ex)
    :(_foo($ex))
end

_foo(n::Int) = n^2

_foo(s::String) = println("Hello ", s, "!")

```

Then you get

```julia
julia> @foo 3
9

julia> @foo "world"
Hello world!

```

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [July 28, 2022, 2:02pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/3 "2022-07-28T14:02:20Z")

</div>

> [@Datseris](#):
>
> `base_type = Core.eval(@ __MODULE__ , base)`

I think this is basically where you’re going wrong. Don’t use `eval` in the body of a macro–it’s going to evaluate that symbol at the time when the macro is expanded, which isn’t actually a particularly useful time, and the thing you’re referring to may not even _have_ a value at that time.

For example, here’s how easily we can break this macro without even involving modules at all:

```julia
julia> let
         mutable struct Foo
           x::Int
         end
         
         @agent Bar Foo begin
           name::String
         end
       end

```

```julia
ERROR: LoadError: UndefVarError: Foo not defined

```

Just wrapping the code in a `let` block is enough to cause the macro to be expanded _before_ `Foo` is defined because, again, you must never use `eval` within the body of a macro.

Instead, your macro needs to _produce an expression_ which does the work you want. For example, if your macro expanded to something like:

```julia
quote
  base_fields = get_base_field_names_and_types(base)
  @eval struct Child
      base_fields
      child_fields
  end
end

```

Then it would work in all circumstances, and you’d never have to think about `@ __MODULE__ ` ever again.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 28, 2022, 2:50pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/4 "2022-07-28T14:50:49Z")

</div>

Thanks @rdeits . I understand your points. The problem I am now is that I do not know how to create this `get_base_field_names_and_types` from `base`. Notice that `base` is just a symbol. That’s exactly why I used `@eval` to transform the Symbol to an actual Type whose fields I can obtain.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 28, 2022, 2:51pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/5 "2022-07-28T14:51:32Z")

</div>

Thanks @cscherrer now I know how to multiple dispatch macros 🙂

---

<div class="post-metadata">

**Author:** ![mikmoore](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mikmoore/32/31109_2.png) [@mikmoore](https://discourse.julialang.org/u/mikmoore)\
**Post date:** [July 28, 2022, 2:54pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/6 "2022-07-28T14:54:35Z")

</div>

This isn’t answering your question, but direct field inheritance isn’t necessarily the “standard” Julia way to do this. Often, composition is preferred and then some access functions or a `getproperty` redefinition can provide the comfortable syntax you’re after:

```julia
struct A
  fld1::Int
end
struct B
  a::A
  fld2::Int
end

# Option 1: define access functions
# this is highly extensible but a little tedious
# the @forward macro from Lazy.jl or MacroTools.jl can make this more convenient
getfld1(a::A) = a.fld1
getfld1(b::B) = getfld1(b.a) # just forward to the field
getfld2(b::B) = b.fld2

# Option 2: redefine getproperty to forward b.fld1 to b.a.fld1
function Base.getproperty(b::B, f::Symbol)
  f == :fld1 && return b.a.fld1
  # also any other properties you want to forward
  return getfield(b, f)
end

# Option 2a: redefine getproperty to forward b.FIELD to b.a.FIELD if possible
# this function might need to be @generated for type stability
function Base.getproperty(b::B, f::Symbol)
  if in(f, propertynames(fieldtype(B, :a)))
    return getproperty(b.a, f)
  end
  return getfield(b, f)
end

# Option 2b: "automatic" property forwarding to the first matching field
# this is almost certainly type-unstable but could be redesigned as @generated
# there is also a slight risk of general chaos
function Base.getproperty(b::B, f::Symbol)
  for i in 1:fieldcount(B)
    if in(f, propertynames(fieldtype(B, i))
      return getproperty(getfield(B, i), f)
    end
  end
  return getfield(b, f)
end

```

I don’t have a Julia session handy so I didn’t have a chance to test the above. There may be some minor mistakes. Also, options 2a and 2b might have performance issues (type instability) unless they are `@generated` so they can resolve `propertynames` at compile time (and possibly unroll the loop in the latter). I didn’t try to write a `@generated getproperty` because I don’t trust myself to get it close to correct without being able to test it.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 28, 2022, 3:01pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/7 "2022-07-28T15:01:42Z")

</div>

Thanks @mikmoore however for my application the “object oriented way” is a much, much more intuitive way to go than defining access functions for every field. I know the later is the “more Julian way”, but, sometimes, the object oriented approach is just straight out better, and this is one of these times. I need to remain within the structs otherwise my users will have to define dozens of functions each time they want to use a new agent type with Agents.jl, which is not justifiable if I am being honest.

---

<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:** [July 28, 2022, 4:09pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/8 "2022-07-28T16:09:28Z")

</div>

Couldn’t you define those dozens of functions as defaults for an abstract supertype, or is this not compatible with `@agents` somehow? (I never used Agents.jl before so idk.) This isn’t like inheritance because concrete types don’t subtype each other, but this is how you get many types to share a method.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 28, 2022, 4:14pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/9 "2022-07-28T16:14:37Z")

</div>

> [@Benny](#):
>
> Couldn’t you define those dozens of functions as defaults

No, because I don’t know in advance what kind of properties the user would want to create for their agent.

---

<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:** [July 28, 2022, 4:28pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/10 "2022-07-28T16:28:52Z")

</div>

I meant the user would design such an abstract supertype and what properties it should have, so they would be defining those functions. For example, if they know all their types will just be an extension of a struct `A`, then they will design an abstract `superA` with subtypes `A, B, C, ...` with default methods `f(::SuperA)` that work on `A`’s properties.

Of course this is very limited, and it’s not feasible if you want to inherit fields from multiple types. Even in languages with multiple inheritance, composition is recommended if you want to “inherit” fields because field name overlap and method resolution order is really messy. Rather than field-wise `getfld1` methods, I would rather define a `getA` or `getB` to get to the contained type and use their methods, like `f(getB(x))`, with a default `f(x) = f(getB(x))` if I needed `f` to work on any type containing `B` (`Lazy.@forward` basically does this in a limited way).

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [July 28, 2022, 7:32pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/11 "2022-07-28T19:32:10Z")

</div>

> [@Datseris](#):
>
> The problem I am now is that I do not know how to create this `get_base_field_names_and_types` from `base`. Notice that `base` is just a symbol.

That’s actually the whole point–`base` is _not_ just a symbol in the context of the code _generated_ by the macro. You’re using `eval` too early (in the body of the macro), which is the whole problem. You have to avoid doing that.

For example, here’s a macro that defines `MyNewType` that just copies the fields of the given type:

```julia
macro copy_type(base)
 quote
   let
     fields = fieldnames($(esc(base)))
     expr = quote
       struct MyNewType
         $(fields...)
       end
     end
     eval(expr)
   end
 end
end

```

Note how the macro body never calls `eval`. The only place where we `eval` is inside the expression generated by the macro itself. That’s the key.

```julia
julia> let 
         struct Bar
           x
           y
           z
         end
         
         @copy_type Bar

         @show fieldnames(MyNewType)
       end

```

```julia
fieldnames(MyNewType) = (:x, :y, :z)
(:x, :y, :z)

```

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 28, 2022, 9:22pm UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/12 "2022-07-28T21:22:22Z")

</div>

Thanks a lot for the help. While I understand a lot more, I still don’t understand everything to actually solve the problem ☹

I’ve managed to continue from your code into a version that obtains the fields and their types. Step 2 now is to allow a custom name for the new struct, and then step 3 would be to allow additional fields to be incorporated… However, I can’t get pass step 2. Here is what I have:

```julia
macro copy_type(base, newname)
  quote
    let
      base_fieldnames = fieldnames($(esc(base)))
      base_types = [t for t in getproperty($(esc(base)), :types)]
      base_fields = [:($f::$T) for (f, T) in zip(base_fieldnames, base_types)]
      expr = quote
        # Somehow enter the actual symbol of `newname` here...
        struct newname
          $(base_fields...)
        end
      end
      eval(expr)
    end
  end
end

mutable struct Example
    id::Int
end

@copy_type Example Example2

```

The problem is, I couldn’t get it to create a struct named `Example2` no matter what I tried. Initially I thought I could just do `$newname`, and that would interpolate the Symbol into the quote. This errors however. Then, I thought, I need to escape `newname` before the inner quote, and then interpolate it in the inner quote. So I did `new_name = :($newname)` before the inner quote, and then used `$(new_name)` in the inner quote. This also didn’t work however. At this point I realize that I am trying random combinations of `$` and `esc`, but I lack the understand to make the conscious choice of the correct combination. I am honestly confused of why the initial idea of just typing `$newname` didn’t work, as when the macro starts `newname` is just a Symbol, so plain interpolation in the expression should “work”…

Perhaps the most confusing thing is that when using simply `$(newname)` in the inner quote, the error it throws is

```julia
ERROR: syntax: invalid type signature around .. / line with `($newname)`

```

which confuses me even more. How is the type signature suddenly affected by this? When I print the expression I get:

```julia
\expr = quote
    #= c:\Users\datse\Desktop\test_inheritance.jl:74 =#
    struct newname
        #= c:\Users\datse\Desktop\test_inheritance.jl:75 =#
        id::Int64
    end
end

```

which seems legit code, even though “newname” isn’t the name of the type I want.

---

<div class="post-metadata">

**Author:** ![rdeits](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rdeits/32/286_2.png) [@rdeits](https://discourse.julialang.org/u/rdeits)\
**Post date:** [July 29, 2022, 2:11am UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/13 "2022-07-29T02:11:36Z")

</div>

Yeah, things get weird with multiple layers of quoting. I think `QuoteNode` is the magic piece you’re missing. Here’s an example:

```julia
julia> macro foo(newname)
         quote
           let
             name = $(QuoteNode(newname))
             expr = quote
               struct $name
                 x
                 y
               end
             end
             eval(expr)
           end
         end
       end
@foo (macro with 1 method)

julia> @foo MyType

julia> @show fieldnames(MyType)
fieldnames(MyType) = (:x, :y)

```

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 29, 2022, 7:00am UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/14 "2022-07-29T07:00:01Z")

</div>

EDIT: This doesn’t work if I define the below macro in a module and call it outside its module. It defines the new type in the module the macro is defined, not the module the macro is called at.

EDIT2: Solved that by using `Core.eval($( __module__ ), expr)` instead of `eval(expr)`

* * *

Thank you very much @rdeits . With your guidance I was able to succeed in what I wanted to do. I post below the full solution for reference. I realized yesterday night that `QuoteNode` was what I needed, because the docs said that that its useful in nested quotes. But I admit, I couldn’t in the end understand _exactly_ how to use it. Maybe I the metaprogramming docs could use more examples. Oh well, in any case, here’s the final product:

```julia

macro copy_type(base_type, newname, extra_fields)
  # This macro was generated with the guidance of @rdeits on Discourse:
  # https://discourse.julialang.org/t/
  # metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912

  # We start with a quote. All macros return a quote to be evaluated
  quote
    let
      # Here we collect the field names and types from the base type
      # Because the base type already exists, we escape the symbols to obtain it
      base_fieldnames = fieldnames($(esc(base_type)))
      base_fieldtypes = [t for t in getproperty($(esc(base_type)), :types)]
      base_fields = [:($f::$T) for (f, T) in zip(base_fieldnames, base_fieldtypes)]
      # Then, we prime the additional name and fields into QuoteNodes
      # We have to do this to be able to interpolate them into an inner quote.
      name = $(QuoteNode(newname))
      additional_fields = $(QuoteNode(extra_fields.args))
      # Now we start an inner quote. This is because our macro needs to call `eval`
      # However, this should never happen inside the main body of a macro
      # There are several reasons for that, see the cited discussion at the top for more
      expr = quote
        struct $name
          $(base_fields...)
          $(additional_fields...)
        end
      end
      # @show expr # uncomment this to see that the final expression looks as desired
      eval(expr)
    end
  end
end

mutable struct Example
  id::Int
end

@copy_type Example Example5 begin
  x::Float64
  y::Float64
end

```

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 29, 2022, 8:39am UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/15 "2022-07-29T08:39:42Z")

</div>

Aaaah the gods really hate me I think. Everything worked perfectly until I did the “very last” step of adding the macro in a module, and also include an abstract type to subtype from. Then, the `QuoteNode` trick does not work any more. Here is an example:

```julia
module TestMacro
export @copy_type

macro copy_type(base_type, newname, supertype)
  quote
    let
      base_fieldnames = fieldnames($(esc(base_type)))
      base_fieldtypes = [t for t in getproperty($(esc(base_type)), :types)]
      base_fields = [:($f::$T) for (f, T) in zip(base_fieldnames, base_fieldtypes)]
      name = $(QuoteNode(newname))
      supertype_quoted = $(QuoteNode(supertype))

      expr = quote
        struct $name <: $supertype_quoted
          $(base_fields...)
        end
      end
      eval(expr)
    end
  end
end

end

using .TestMacro

abstract type Abstract end

mutable struct Example
    id::Int
end

@copy_type Example Example2 Abstract

```

that errors with `ERROR: UndefVarError: Abstract not defined`.

I also tried to escape the abstract type, since it has already been defined, by doing `$$(esc(supertype))` in place of where I currently have `$supertype_quoted`. This led to something really weird: the code no longer errored, but, oddly, `Example2` wasn’t defined after I’ve run the macro. I still don’t understand how this broke the macro and it doesn’t evaluate `Example2` anymore.

EDIT: Actually, forget about the Subtyping business. If I put the macro in a module, then even the version I posted above in the previous reply, that works perfectly, does NOT define `Example2`. I think the macro only defines these types in the module _it_ was defined. But I want the opposite: I want these types to be defined in the module the macro is _called in_.

---

<div class="post-metadata">

**Author:** ![Datseris](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/datseris/32/13406_2.png) [@Datseris](https://discourse.julialang.org/u/Datseris)\
**Post date:** [July 29, 2022, 8:55am UTC](https://discourse.julialang.org/t/metaprogramming-obtain-actual-type-from-symbol-for-field-inheritance/84912/16 "2022-07-29T08:55:22Z")

</div>

Okay! Solved the issue of `Example2` not being defined by adding `Core.eval($( __module__ ), expr)` instead of just `eval(expr)`.

The solution for the subtyping then become the `$$(esc(supertype))` !!!
