I thought I had seen most things in julia, but I’ve come across the following construction which I don’t think I’ve seen in the docs:
using MacroTools
const gotit = Set()
macro genconstructor(spec)
if !@capture(spec, function name_{T__}(args__) where TV__ body_ end)
error("error")
end
ename = esc(name)
newspec = quote
if !$(name in gotit)
$(push!(gotit, name))
function $ename(args...; kw...)
call_some_constructor(args...)
end
end
end
quote
$(esc(spec))
$newspec
end
end
struct Foo{T}
a::T
@genconstructor function Foo{T}(x) where T
Foo(x)
end
@genconstructor function Foo{T}(x::T) where T
Foo(x+1)
end
end
julia> methods(Foo)
# 1 method for type constructor:
[1] Foo(args...; kw...)
julia> methods(Foo{Int})
# 2 methods for type constructor:
[1] Foo{T}(x::T) where T
[2] Foo{T}(x) where T
I.e. the macro @genconstructor takes a function, a constructor, looking like
function Foo{T}(args) where T
...
end
It outputs the same function definition, and conditionally, a slightly different definition, so the full output of the macro is two constructors, the original and a simplified one:
function Foo{T}(args) where T
...
end
if !(:Foo in gotit)
push!(gotit, :Foo)
function Foo(args...; kw...)
dosomething()
end
end
The point with the test and the gotit Set, is to avoid defining the extra constructor repeatedly if the macro is used more than once in the same struct (which would lead to a warning, and prevent precompilation).
Now, my question is, what are the rules for executing statements inside a struct definition? Can local variables be made there? Is it just like any other local scope, except for definition of functions named like the struct, which becomes inner constructors?
Are things like this officially supported?:
struct Bar
a::Int
local b = Ref(0)
Bar(n) = (e = new(n+b[]); b[] += n; e)
end
julia> Bar(1)
Bar(1)
julia> Bar(1)
Bar(2)
julia> Bar(1)
Bar(3)
julia> Bar(1)
Bar(4)