# Fully lost with custom broadcasting interface

**URL:** <https://discourse.julialang.org/t/fully-lost-with-custom-broadcasting-interface/69140>\
**Category:** General Usage\
**Tags:** broadcast, interface\
**Created:** [October 3, 2021, 1:29pm UTC](https://discourse.julialang.org/t/fully-lost-with-custom-broadcasting-interface/69140 "2021-10-03T13:29:49Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![tpoisot](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tpoisot/32/19812_2.png) [@tpoisot](https://discourse.julialang.org/u/tpoisot)\
**Post date:** [October 3, 2021, 1:29pm UTC](https://discourse.julialang.org/t/fully-lost-with-custom-broadcasting-interface/69140/1 "2021-10-03T13:29:49Z")

</div>

I am trying to implement custom broadcasting for a type, and after spending a few hours with [the doc](https://docs.julialang.org/en/v1/manual/interfaces/#man-interfaces-broadcasting) I am well and truly lost.

My type looks like this:

```julia
struct XYZ{T}
    data::Matrix{Union{T,Nothing}}
    x::Number
    y::Number
end

```

I am mostly accessing elements in `data` using Cartesian indexing, but the very important point is that I would like the values with `nothing` to be omitted (it’s not a sparse array because 99% of the time the degree of sparsity makes them less efficient, and because `T` can be an arbitrary type).

I have tried to define

```julia
Base.size(R::XYZ) = size(R.data)
Base.length(R::XYZ) = length(R.data)
Base.getindex(R::XYZ{T}, inds::Vararg{Int,2}) where {T} = R.data[inds...]
Base.setindex!(R::XYZ{T}, val, inds::Vararg{Int,2}) where {T} = R.data[inds...] = val
Base.broadcastable(R::XYZ) = R
Base.BroadcastStyle(::Type{XYZ}) = Broadcast.Style{XYZ}()
Base.axes(R::XYZ) = tuple(findall(!isnothing, R.data))

```

and then

```julia
function Base.similar(bc::Broadcast.Broadcasted{Broadcast.Style{XYZ}}, ::Type{ElType}) where ElType
    A = find_entry(bc)
    XYZ(similar(Array{Union{Nothing,ElType}}, size(bc)), A.x, A.y)
end

```

where `find_entry` is inspired by the docs:

```julia
find_entry(bc::Base.Broadcast.Broadcasted) = find_entry(bc.args)
find_entry(args::Tuple) = find_entry(find_entry(args[1]), Base.tail(args))
find_entry(x) = x
find_entry(::Tuple{}) = nothing
find_entry(r::XYZ, rest) = r
find_entry(::Any, rest) = find_entry(rest)

```

But then I get the following error message:

```julia
julia> x .+ 1
ERROR: ArgumentError: broadcasting requires an assigned BroadcastStyle
Stacktrace:
 [1] copy(bc::Base.Broadcast.Broadcasted{Base.Broadcast.Unknown, Tuple{Vector{CartesianIndex{2}}}, typeof(+), Tuple{XYZ{Int64}, Int64}})
   @ Base.Broadcast ./broadcast.jl:899
 [2] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.Unknown, Nothing, typeof(+), Tuple{XYZ{Int64}, Int64}})
   @ Base.Broadcast ./broadcast.jl:883
 [3] top-level scope
   @ REPL[146]:1

```

There’s probably something obvious I am missing, but I’m 100% lost on this one…

---

<div class="post-metadata">

**Author:** ![goerch](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goerch/32/29122_2.png) [@goerch](https://discourse.julialang.org/u/goerch)\
**Post date:** [October 3, 2021, 2:41pm UTC](https://discourse.julialang.org/t/fully-lost-with-custom-broadcasting-interface/69140/2 "2021-10-03T14:41:43Z")

</div>

The following modification works for me

```julia
struct XYZ{T}
    data::Matrix{Union{T,Nothing}}
    x::Number
    y::Number
end

Base.size(R::XYZ) = size(R.data)
Base.length(R::XYZ) = length(R.data)
Base.getindex(R::XYZ{T}, inds::Vararg{Int,2}) where {T} = R.data[inds...]
Base.setindex!(R::XYZ{T}, val, inds::Vararg{Int,2}) where {T} = R.data[inds...] = val
Base.broadcastable(R::XYZ) = R
Base.BroadcastStyle(::Type{XYZ}) = Broadcast.Style{XYZ}()
Base.axes(R::XYZ) = tuple(findall(!isnothing, R.data))

function Base.similar(bc::Broadcast.Broadcasted{Broadcast.Style{XYZ}}, ::Type{ElType}) where ElType
    A = find_entry(bc)
    XYZ(similar(Array{Union{Nothing,ElType}}, size(bc)), A.x, A.y)
end

x = XYZ{Float64}(ones(1, 1), 1, 1)
println(x.data .+ 1)

```

Is this what you intended?

---

<div class="post-metadata">

**Author:** ![tpoisot](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tpoisot/32/19812_2.png) [@tpoisot](https://discourse.julialang.org/u/tpoisot)\
**Post date:** [October 3, 2021, 8:22pm UTC](https://discourse.julialang.org/t/fully-lost-with-custom-broadcasting-interface/69140/3 "2021-10-03T20:22:37Z")

</div>

> [@goerch](#):
>
> Is this what you intended?

I was looking for something like `x .+ 1` (I got this one to work), or even `x .+= 2.0` (which I am still struggling with)

---

<div class="post-metadata">

**Author:** ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)\
**Post date:** [October 3, 2021, 8:48pm UTC](https://discourse.julialang.org/t/fully-lost-with-custom-broadcasting-interface/69140/4 "2021-10-03T20:48:47Z")

</div>

if you want to broadcast like an array, you better make it that way:

```julia
julia> struct XYZ{T} <: AbstractArray{T, 2}
           data::Matrix{Union{T,Nothing}}
           x::Number
           y::Number
       end

julia> Base.BroadcastStyle(::Type{<:XYZ}) = Broadcast.ArrayStyle{XYZ}()

julia> Base.size(A::XYZ) = size(A.data)

julia> Base.length(A::XYZ) = length(A.data)

julia> Base.getindex(A::XYZ{T}, inds::Vararg{Int,N}) where {T,N} = A.data[inds...]

julia> Base.setindex!(R::XYZ{T}, val, inds::Vararg{Int,2}) where {T} = R.data[inds...] = val

julia> function Base.similar(bc::Broadcast.Broadcasted{Broadcast.ArrayStyle{XYZ}}, ::Type{ElType}) where ElType
           A = find_entry(bc)
           XYZ(similar(Array{Union{Nothing,ElType}}, size(bc)), A.x, A.y)
       end

julia> find_entry(x) = x
julia> find_entry(r::XYZ, rest) = r
julia> find_entry(bc::Base.Broadcast.Broadcasted) = find_entry(bc.args)
julia> find_entry(args::Tuple) = find_entry(find_entry(args[1]), Base.tail(args))

julia> XYZ(Matrix{Union{Float64, Nothing}}(ones(2,2)), 1, 2) .+ 1
2×2 XYZ{Float64}:
 2.0 2.0
 2.0 2.0

```
