I was wondering if it is possible to expand or get fieldnames of a DataType inside a macro. My instinct says it is not possible, but maybe someone knows a trick or something…
struct DummyStruct
a
b
end
# return somehow the fieldnames in DummyStruct
@dummymacro(DummyStruct)
Please, notice that I want to build a function expression inside the macro using those fieldnames and that is why I need to get those fieldnames in the macro scope.
No. The macro only sees the symbol :dummyvariable but not what it points to (this is undefined at macro evaluation time). But the macro can create code which uses fieldnames to look up field-names.
Hi @Oscar_Smith and @mauro3. Thanks for your reply. I get your suggestion, but I need those fieldnames to build the expression of a function, I cannot use fieldnames inside the function to solve my problem.
For example, I want to create a function that uses handlers for variables defined in a struct in order to be able to evaluate an expression:
p -> begin
# expand fieldnames in p
a = p.a
b = p.b
# This expression is given by the user and I don't want to operate on it
# because it might involve many things, i.e. I don't want to change it to
# p.a + p.b
return a + b
end
Using fieldnames in this function does not solve the problem.
That looks like a situation, where you might want to use a generated function instead of a macro. At least to me, that seems more idiomatic than calling eval inside a macro.
The macro is, in general, expanded before the struct exists and certainly before the symbol being passed into the macro has a value. This just isn’t something macros can do for you, at least not in a way you can rely on.
An @generated function is probably the solution here, although it’s hard to know without knowing what you’re actually trying to accomplish. Can you give any more context?
I thought that parametric methods are specialized in a way that you have “unrolled” fieldnames code on types that are in method parmeters. Just because nothing prevents from optimizing it at compile time.
What about @jw3126response? It seems correct to me. Can you check it out?
Yes, of course. I want to create a function expression inside a macro by receiving a DataType and a expression that needs to be evaluated using those attributes inside the struct.
So for example:
# define the struct with defaulted values
@with_kw struct foo{T}
a::T = 1
b::T = 2
end
f = @bar begin
params: foo
expr: a + b
end
where the macro bar builds the function expression:
foo -> begin
# expand fieldnames in p
a = foo.a
b = foo.b
# This expression is given by the user and I don't want to operate on it
# because it might involve many things, i.e. I don't want to change it to
# foo.a + foo.b
return a + b
end
I know that I can define the parameters inside the macro bar, which will allow me to know the parameters. However, I wanted to make them separatelye because they might be in separate windows in the front end (User Interface).
Sorry, I’m on a phone right now so I can’t run code, but try something like this:
function foo(T)
@doit(T)
end
The macro just gets the symbol T. It has no access to the value that T is bound to when you call foo. After all, you haven’t even called foo yet, so how could it?
The given example happens to work because it only involves global variables that were already defined at the time the macro was expanded. You can’t rely on that being the case.
As for your actual task, am I correct in assuming that the expression a + b is coming in as run-time data, such as a parsed string from a user interface? If so, then you probably need to call eval on something because there’s no other way to cause a function to come into existence at run-time.
A related question is: are the symbols inside the expression always supposed to be fields of the struct? If so, can you just transform every symbol a into foo.a? In that case, you don’t need to know the field types of anything. I suspect that’s probably too simple for your real application, but it’s worth considering.
I’m not sure I understand the distinction you’re making. In any case, it’s easy to find examples that break it:
julia> macro doit(T)
fnames = fieldnames(eval(T))
# better:
# fieldnames(Core.eval(__module__, T))
@show fnames
end
@doit (macro with 1 method)
julia> function foo(T)
@doit(T)
end
ERROR: LoadError: UndefVarError: T not defined
Note the error occurs before we even try to call foo.
Yeah, I get that. It is not possible to define that function at all. But the example should be something like:
julia> macro doit(T)
fnames = fieldnames(eval(T))
# better:
# fieldnames(Core.eval(__module__, T))
@show fnames
end
@doit (macro with 1 method)
julia> struct bar
a
b
end
julia> function foo(x::bar)
@doit(bar)
end
fnames = (:a, :b)
foo (generic function with 1 method)
julia> foo(bar(1,2))
(:a, :b)
Anyway, I get your concern (the code above is not the best). Let me first figure out which path I decide to follow to build my macro. Once I get that, I will post it. I have collected many useful ideas to process.