I’d like to customize broadcasting for a type, such that Julia will allocate the result using a StructArray
from the StructArrays.jl package instead of a plain array. This is actually very easy to do:
julia> struct Foo{Ta,Tb,Tc}
a::Ta
b::Tb
c::Tc
end
julia> using StructArrays: StructArray
julia> function Base.similar(bc::Broadcast.Broadcasted{Broadcast.DefaultArrayStyle{N}}, ::Type{ElType}) where {N,ElType<:Foo}
return StructArray{ElType}(undef, size(bc))
end
julia> foos = Foo.(1:10, 2:11, 3:12)
10-element StructArray(::Vector{Int64}, ::Vector{Int64}, ::Vector{Int64}) with eltype Foo{Int64, Int64, Int64}:
Foo{Int64, Int64, Int64}(1, 2, 3)
Foo{Int64, Int64, Int64}(2, 3, 4)
Foo{Int64, Int64, Int64}(3, 4, 5)
Foo{Int64, Int64, Int64}(4, 5, 6)
Foo{Int64, Int64, Int64}(5, 6, 7)
Foo{Int64, Int64, Int64}(6, 7, 8)
Foo{Int64, Int64, Int64}(7, 8, 9)
Foo{Int64, Int64, Int64}(8, 9, 10)
Foo{Int64, Int64, Int64}(9, 10, 11)
Foo{Int64, Int64, Int64}(10, 11, 12)
julia> foos[1]
Foo{Int64, Int64, Int64}(1, 2, 3)
julia> foos.b
10-element Vector{Int64}:
2
3
4
5
6
7
8
9
10
11
julia>
The method for similar
takes inspiration from this line in broadcast.jl
for Bool
: julia/base/broadcast.jl at 860188fb617a2a26042a5d6b64b2e40c960c3ccb · JuliaLang/julia · GitHub
But, what if I feel like creating a plot afterword with Makie?
julia> using GLMakie
julia> fig = Figure()
ERROR: UndefVarError: `T` not defined in static parameter matching
Suggestion: run Test.detect_unbound_args to detect method arguments that do not fully constrain a type parameter.
Stacktrace:
[1] buildfromschema(f::Function, ::Core.TypeofBottom)
@ StructArrays ~/.julia/packages/StructArrays/n5wxA/src/lazy.jl:55
[2] #_#25
@ ~/.julia/packages/StructArrays/n5wxA/src/structarray.jl:206 [inlined]
[3] StructArray
@ ~/.julia/packages/StructArrays/n5wxA/src/structarray.jl:205 [inlined]
[4] similar
@ ./REPL[7]:2 [inlined]
[5] copy
@ ./broadcast.jl:902 [inlined]
[6] materialize
@ ./broadcast.jl:867 [inlined]
[7] Attributes(pairs::Vector{Pair{Symbol, Union{}}})
@ MakieCore ~/.julia/packages/MakieCore/yRxVU/src/attributes.jl:31
[8] Attributes
@ ~/.julia/packages/MakieCore/yRxVU/src/attributes.jl:32 [inlined]
[9] Scene(; viewport::Nothing, events::Events, clear::Bool, transform_func::Function, camera::typeof(campixel!), camera_controls::EmptyCamera, transformation::Transformation, plots::Vector{…}, children::Vector{…}, current_screens::Vector{…}, parent::Nothing, visible::Observable{…
}, ssao::SSAO, lights::MakieCore.Automatic, theme::Attributes, deregister_callbacks::Vector{…}, theme_kw::@Kwargs{})
@ Makie ~/.julia/packages/Makie/6KcTF/src/scenes.jl:231
[10] Figure(; kwargs::@Kwargs{})
@ Makie ~/.julia/packages/Makie/6KcTF/src/figures.jl:112
[11] Figure()
@ Makie ~/.julia/packages/Makie/6KcTF/src/figures.jl:108
[12] top-level scope
@ REPL[13]:1
Some type information was truncated. Use `show(err)` to see complete types.
julia>
Not great!
But if I change the definition of similar
slightly:
julia> struct Foo{Ta,Tb,Tc}
a::Ta
b::Tb
c::Tc
end
julia> using StructArrays: StructArray
julia> function Base.similar(bc::Broadcast.Broadcasted{Broadcast.DefaultArrayStyle{N}}, ::Type{Foo{Ta,Tb,Tc}}) where {N,Ta,Tb,Tc}
return StructArray{Foo{Ta,Tb,Tc}}(undef, size(bc))
end
julia> foos = Foo.(1:10, 2:11, 3:12)
10-element StructArray(::Vector{Int64}, ::Vector{Int64}, ::Vector{Int64}) with eltype Foo{Int64, Int64, Int64}:
Foo{Int64, Int64, Int64}(1, 2, 3)
Foo{Int64, Int64, Int64}(2, 3, 4)
Foo{Int64, Int64, Int64}(3, 4, 5)
Foo{Int64, Int64, Int64}(4, 5, 6)
Foo{Int64, Int64, Int64}(5, 6, 7)
Foo{Int64, Int64, Int64}(6, 7, 8)
Foo{Int64, Int64, Int64}(7, 8, 9)
Foo{Int64, Int64, Int64}(8, 9, 10)
Foo{Int64, Int64, Int64}(9, 10, 11)
Foo{Int64, Int64, Int64}(10, 11, 12)
julia> foos.a
10-element Vector{Int64}:
1
2
3
4
5
6
7
8
9
10
julia>
Makie works again.
Could someone explain what’s going on there?