abstract type AbstractFoo{T}; end
struct SomeFoo <: AbstractFoo{Integer}
field1::Integer
field2::Vector{Integer}
end
struct OtherFoo <: AbstractFoo{Float64}
field1::Float64
field2::Vector{Float64}
field3::Dict{Symbol, Float64}
end
Is it somehow possible to use the Integer & Float64 from the first line of the type definition in the field types? i.e., something like (obviously not working)?
struct SomeFoo <: AbstractFoo{T} where T=Integer
field1::T
field2::Vector{T}
end
Alternatively, any other way of enforcing all of the fields containing T to be equal to Integer, by typing it only once?
or any other function, which necessarily needs an Integer as field1. As I have a lot of Foos, I would like to make sure that I do not make some sort of ‘typo’ and create
struct OneMoreFoo <: AbstractFoo{Integer}
field1::Float64
end
by mistake. Therefore I am looking for a coding pattern, which disallows this.
While your solution does solve the issue, SomeFoo is actually a parametric type, while I would like to have it parameter-less.
Maybe the cleanest solution is to create a macro, which does this for me.
@Raf’s solution does not conflict with this goal: in fact, it prevents “typos” of this kind, and as for the first problem, you can still restrict with an appropriate method signature, or check the type inside the function with @assert or a type assertion ::Integer.
I have checked some of my code, and found out that the proposed solution fails on a lot of cases of this form
abstract type AbstractFoo{T}; end
Base.@kwdef struct SomeFoo{T<:Integer} <: AbstractFoo{T}
field::Vector{T} = Vector{T}()
end
julia> sf = SomeFoo()
ERROR: UndefVarError: T not defined
julia> sf = SomeFoo{Integer}()
SomeFoo{Integer}(Integer[])
The point is that the proposed solution only works because it is able to infer the type of SomeFoo automatically. If I do
Base.@kwdef struct OtherFoo <: AbstractFoo{Integer}
field2::Vector{Integer} = []
end
julia> of = OtherFoo()
OtherFoo(Integer[])
everyhing works nicely, but I have the Integer there multiple times again.
The solution through the type assertions could work as well, but it would be much nicer if the automatic type assertions checked this for me, without me having to hard-code all of them.
I understand that this is a niché question, so if there is no straightforward solution, I will simply move on with one of the proposed ones and my code will not be much worse. But it would be nice to have
The keyword function is run in a different scope, so It doesn’t “see” the T. You need to use a specific type in the default value, on the right hand side of the =.\
To get around that, define a constructor with default value as a kw arg.
True. Do you have any idea how to do this? This approach
julia> Base.@kwdef struct SomeFoo{T<:Integer} <: AbstractFoo{T}
field::Vector{T} = []
end
julia> sf = SomeFoo()
ERROR: MethodError: no method matching SomeFoo(::Array{Any,1})
fails and the other possibility of
julia> Base.@kwdef struct OtherFoo{T<:Integer} <: AbstractFoo{T}
field::Vector{T} = Integer[]
end
julia> of = OtherFoo()
OtherFoo{Integer}(Integer[])
is again using the Integer inside the fields, which did not help.
Can you give me an example? I feel like this does not solve the duplicite Integer issue, rather only pushes the problem to the constructor, but maybe I am wrong.
You can get Integer from SomeFoo.var.ub but it’s still going to be a bit of boilerplate. And you can put that in the kwdef too. I was imagining you could do it for AbstractFoo but its not so easy.