Programatically get return type of `broadcast`?

Is there some way to get what a call to broadcast would return, possibly similar to how promote works? An example of what I need is something like this:

using StaticArrays

a1 = SA_F32[1, 2, 3] # SArray{Tuple{3}, Float32, 1, 3}
a2 = SA_F32[1 2 3 4] # SArray{Tuple{1, 4}, Float32, 2, 4}
@assert typeof(a1 .+ a2) == SArray{Tuple{3, 4}, Float32, 2, 12}
@assert typeof(a1 .+ a2) == [some function?](typeof(a1), typeof(b1))
                           # ^^^^^^^^^^^^^^

Have you tried the promote_type(T, U) function?

Unfortunately, it does not provide the correct result:

julia> promote_type(typeof(a1), typeof(a2))
SArray{S,Float32,N,L} where L where N where S<:Tuple

julia> promote_type(typeof(a1), typeof(a2)) == SArray{Tuple{3,4}, Float32, 2, 12}
false

They are not equal, however,

promote_type(typeof(a1), typeof(a2)) == SArray{Tuple{3,4}, Float32, 2, 12}
false

should return true, I guess?

Note the problem is that promote_type(typeof(a1),typeof(a2)) in this case is abstract but the RHS is concrete. You do have

julia> typeof(a1 .+ a2) <: promote_type(typeof(a1),typeof(a2))
true
2 Likes

I’m sorry I was also trying to paste this but forgot to change it :smiley:

promote_type(typeof(a1), typeof(a2)) <: SArray{Tuple{3,4}, Float32, 2, 12}

This should return true?

Makes sense. Do you know if there is a way to get the specific type without doing the actual op? I’ve meanwhile come up with the following hacky way to get it to work, but I’m not knowledgable enough about Julia to identify its shortcomings:

function vector_op(a, b)
    @. a + b
end

function promote_vectorized(a::DataType, b::DataType)::DataType
    Base.promote_op(vector_op, a, b)
end

@assert typeof(a1 .+ a2) == promote_vectorized(typeof(a1), typeof(a2))

For my use case, I think this will work well enough. To briefly summarize, I am stringing together pieces of user-provided code and inserting type checks in between so that if one piece of code is written incorrectly, a nice error is produced before moving on to the other pieces of code, so the error can more easily be identified. I want to use it like this:

const StereoBuffer = SArray{Tuple{2, 512}, Float32, 2, 1024}
# Check that some_var can be used in math operations with other audio data.
@assert promote_vectorized(typeof(some_var), StereoBuffer) == StereoBuffer

Using this you get the dimension of the resulting array:

julia> StaticArrays.combine_sizes(StaticArrays.broadcast_sizes(a1,a2))
Size(3, 4)

which corresponds to this operation on the broadcast:

julia> StaticArrays.broadcast_size(a1 .+ a2)
Size(3, 4)

1 Like

This looks really promising, I think combined with eltype() it would be perfect. I’ll test it out later today.

I found that by following the path of what a1 .+ a2 does, with:

@edit broadcast(+,a1,a2)

and seeing the implementation. I could not find exactly how to get the complete output type, though. But it should be possible.

1 Like

I just discovered it is possible to directly use Base.promote_op on broadcast:

ty = Base.promote_op(Base.broadcast, typeof(+), typeof(a1), typeof(a2))
@assert ty == typeof(a1 .+ a2)

I think this provides the most idiomatic solution.