Getting parent's parametric types parameter

Hey there,

I have some code of this form:

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?

You mean like this?

struct SomeFoo{T<:Integer} <: AbstractFoo{T}
  field1::T 
  field2::Vector{T}
end

julia> SomeFoo(1, [1, 2])
SomeFoo{Int64}(1, [1, 2])

I should also ask why you need the limitation at all? This would stop people using integers or floats with Unitful units, or similar things…

The point is that somewhere in the code, there is a function like this:

useint(foo::AbstractFoo{Integer}) = factorial(foo.field1)

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.

I don’t understand what you mean here. A parametric type can’t be parameter-less?

Also saying “macro” and “cleanest solution” in the same sentence should be a warning sign :wink:

@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 guess you got me on this one :slight_smile:

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 :slight_smile:

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.

1 Like

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.

Time for that macro :slight_smile:

1 Like

Yep, it seems so :slight_smile:

Thanks for the effort anyway, already learned a lot!

Or probaly just live with the boilerplate for a while