I personally always need a second more to understand do
blocks than “normal” functions, but I think you have good reasons to do it like that – depending on how complex your actual struct is, this is the easiest to read for me, but I would also be curious about other ways to approach this.
But regarding your second question: With @macroexpand
you can check the exact code that will be generated and see what will actually be stored in the struct:
expanded macro
(I removed everything after the struct definition and added some empty lines to make it more readable; also the calculation of ys
is just a dummy of course)
@macroexpand @with_kw struct X @deftype Float64
a; b; c; d; e; f; g
xv::Vector{Float64}
yv::Vector{Float64} = map(xv) do x
x * a + b * c + d * e * f * g
end
end
begin
$(Expr(:meta, :doc))
struct X
a::Float64
b::Float64
c::Float64
d::Float64
e::Float64
f::Float64
g::Float64
xv::Vector{Float64}
"Default: map(xv) do x\n #= REPL[24]:8 =#\n x * a + b * c + d * e * f * g\nend"
yv::Vector{Float64}
X(; a = #= remaining fields, edited out =# , yv = map(xv) do x
#= REPL[24]:8 =#
x * a + b * c + d * e * f * g
end) = begin
X(a, b, c, d, e, f, g, xv, yv)
end
X(a, b, c, d, e, f, g, xv, yv) = begin
new(a, b, c, d, e, f, g, xv, yv)
end
end
end
So the function to generate ys
is “stored” just inside the constructor like X(; ..., ys = anonymous_fn(...))
, but the struct just contains the plain values that come out of the computation. Note that I had to annotate ys::Vector{Float64}
because of the @deftype Float64
.