Hello everyone,
I was wondering if there was a way to define a macro @define that, given
let
a::Float64 = 0.5
b::Int64 = 1
@define MyStruct a b
end
would define a struct MyStruct with fields a::Float64 and b::Int64, as much as the following code:
let
a::Float64 = 0.5
b::Int64 = 1
struct MyStruct
a::typeof(a)
b::typeof(b)
end
end
This would define a MyStruct which types are correctly represented as svec(Float64, Int64).
My naive approach to the macro is of text replacement, but it doesn’t work, as only the Symbols a and b are passed in, without any information about their types. The result is that the types of a defined struct always look like svec(Any, Any)
macro define(name, variables...)
local variables_with_types = []
for i = 1 : length(variables)
local current_var_with_type = Symbol("$(variables[i])::typeof($(variables[i]))")
push!(variables_with_types, current_var_with_type)
end
local struct_definition = quote
struct $name
$(variables_with_types...)
end
end
return esc(:($struct_definition))
end
Is there a way to achieve such task? Or must the macro be written in the form @define MyStruct (a, Float64) (b, Int64)?
Macros operate on syntax, not on values. The fact that the macro only sees the symbols :a and :b is by design. That’s what macros do: they take syntax and turn it into other syntax.
You could, if you really want, create a macro that outputs a call to eval() to define the struct based on the actual values (and thus, types) of :a and :b in whatever scope that macro was called in but…I really wouldn’t recommend it. There’s almost certainly a cleaner way to achieve whatever your actual goal is.
Perhaps if you can explain more about what your goal with this macro is, perhaps we can help you find a simpler way of achieving it.
Thank you for the answer. I am currently developing a package where I would like to provide an interface that looks like this:
@define MyObject begin
a::Int64 = 2
b::Float64 = 1.0
whatever_variable::Float64 = 2.3
c::AnotherObject = AnotherObject(whatever_variable)
@new(a, b, c)
end
which should expand to (or equivalent):
struct MyObject
a::Int64
b::Float64
c::CustomStruct
function MyObject()
a::Int64 = 2
b::Float64 = 1.0
whatever_variable::Float64 = 2.3
c::AnotherObject = AnotherObject(whatever_variable)
return new(a, b, c)
end
end
There is no special need for me to have the user to provide the name of the struct. So, if it easier, the interface could just look like: @define begin...end
Yes, that looks perfectly doable, since all of the information (i.e. the names and types of a and b) are contained within the input to the macro. In fact, it looks like what you’re trying to do is very close to what Parameters.jl already does: https://github.com/mauro3/Parameters.jl Does that package fit your needs?
It definitely looks close to what I need. But how could I go about having extra variables that are used for the initialization of others (e.g. the whatever_variable in my previous example), without including them in the struct? Also, is there a way for me to abstract the keyword struct away from the macro, so that the user can just write @define begin...end? Sorry for the amount of questions, I am still a learner in the macros world