Isbits Without Propagation of Parametric Types?

Consider this MWE:

abstract type A end
abstract type B <: A end
abstract type C <: A end

struct D{T <: A}
    data::UInt8
end

struct E
    a::D
    b::D
    c::D
end

struct F{T1 <: D, T2 <: D, T3 <: D}
    a::T1
    b::T2
    c::T3
end

@assert !isbits(E(D{B}(0x00), D{C}(0x00), D{B}(0x00)))
@assert isbits(F{D{B}, D{C}, D{B}}(D{B}(0x00), D{C}(0x00), D{B}(0x00)))

Is it possible to modify E to be isbits without having to specify all of the types within it? In the real example, I basically have two layers that contain several E’s that would otherwise be isbits, and I never use the parameters in function specialization except when passing a, b, and c themselves into functions. I’m new to parametric types, so this might not make sense

I concur, I don’t understand what the question is.

2 Likes

This sounds like an XY problem - Wikipedia to me. Can you be more specific?

2 Likes

D on its own is not a concrete type, it’s a UnionAll, whereas the parametrized version is a concrete type:

julia> typeof(D)
UnionAll        

julia> typeof(D{B})
DataType             

julia> isconcretetype(D{B})
true                       
                           
julia> isconcretetype(D)   
false                      

Julia does not know just from looking at the field in E that you’re never using the parameter of D inside of D - however, that has no bearing on what you could do with it in a function though. Consider (I believe that’s what you’re doing, yes?):

julia> f(e) = g(e.a)               
f (generic function with 1 method) 
                                   
julia> g(::D{B}) = "B"             
g (generic function with 1 method) 
                                   
julia> g(::D{C}) = "C!"            
g (generic function with 2 methods)
                                   
julia> f(E(D{B}(0x0^C              
                                   
julia> struct F # just to make this a little easier to read
           a::D                    
       end                         
                                   
julia> f(F(D{B}(0x0)))             
"B"                                
                                   
julia> f(F(D{C}(0x0)))             
"C!"                               

julia> isbitstype(F)
false               

In other words, the type information of its parameter isn’t lost when only specifying D in E (or another struct for that matter). The result is that the field has to be boxed to accomodate the extra type tag for dynamic dispatch.

If you check @code_warntype f(F(D{C}(0x0))), you’ll also see that julia tells you about only knowing the UnionAll D and marks it red to signify that this will cause dynamic dispatch.

2 Likes

Apologies! I’ll try to elaborate

I agree. Here’s the real (Y) problem. I was inspired by this post (Julia used Multiple Dispatch! It's Super Effective! - Moll.dev) to try to use multiple dispatch for effectiveness in a full Pokemon model. The problem is that in the full model, types have their own moves, and Pokemon themselves can have one or two types. My solution is contained in team/damage.jl and team/data.jl of this repository: GitHub - pvpisistratus/RandomBattles.jl: PoGo PvP Battles simulated with random players, or models based on random players

This implementation still re-uses a lot of code (maybe it doesn’t have to, but that’s a different question), but more importantly it comes with a steep performance hit and also makes the entire StaticState not able to be isbits (my assumption is that the two are related).

If I’m understanding this correctly, this means that since all of the type information must be specified to be isbits, aside from adding enough parameters for the moves and pokemon (30), I should probably go back to making the Pokemon types a field of the data type again