Consider a definition with the outer-only constructor
Base.@kwdef struct Foo{T,S}
a::T
b::S
function Foo(a::T, b::S) where {T,S}
@assert a > 0 && b > 0
new{T,S}(a, b)
end
end
where T
and S
are not used for anything, just parametrizing new
. An alternative would be
Base.@kwdef struct Foo2{T,S}
a::T
b::S
function Foo2(a, b)
@assert a > 0 && b > 0
new{typeof(a),typeof(b)}(a, b)
end
end
Is there a way to get rid of the boilerplate? I tried
Base.@kwdef struct Foo3{T,S}
a::T
b::S
function Foo3(a, b)
@assert a > 0 && b > 0
args = (a, b)
new{map(typeof, args)...}(args...)
end
end
but got
ERROR: syntax: too few type parameters specified in "new{...}"
1 Like
I encountered this limitation before and could not figure out a way to automate the process. I think something along the lines of what you proposed would be a useful addition to the language.
I don’t think one can make it entirely automatic, consider eg
Base.@kwdef struct Foo{S,T} # NOTE ORDER
a::T
b::S
function Foo(a::T, b::S) where {T,S}
@assert a > 0 && b > 0
new{S,T}(a, b) # NOTE ORDER
end
end
What is missing is a nice way to compose
-
the outermost layer of keyword arguments, with defaults,
-
the default outer constructor which nicely promotes,
-
an inner constructor which can call an arbitrary block of code that has at least access to the fields (not necessarily transformations, generation, etc, even though that would be nice).
Currently Base
provides an all-or-nothing approach. Which may be fine, this is a job for a package. Currently none of the options available provide a full solution. But the problem is quite complex and I need to think more about what I want exactly, and whether it can be generalized enough to be packaged.
The immediate question I don’t get though is why the Foo3
inner constructor code doesn’t work. Is it a bug?
Unrelated (and most likely just a slip): @assert
in your code should be replaced by throwing an exception: see the warning box in the doc.
I don’t know if it’s a bug, but it seems to be unsupported. Without @kwdef
, I get this:
Julia-1.1.0> struct Foo3{T,S}
a::T
b::S
function Foo3(a, b)
@assert a > 0 && b > 0
args = (a, b)
new{map(typeof, args)...}(args...)
end
end
ERROR: syntax: ... is not supported inside "new"
1 Like
Thanks, there was even an issue:
1 Like