Metaprogramming: Obtain actual type from Symbol for field inheritance

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:


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
1 Like