This does need to be documented better. In the meantime, consider the following pointers that may help:
-
@davidanthoff’s implementation of
broadcast
for DataValues.jl: https://github.com/davidanthoff/DataValues.jl/pull/9/files
This is a lightly edited version of a “minimal working example” I posted on Gitter, during a discussion with @ChrisRackauckas :
Depending on your use case you can choose to use as much or as little of the generic broadcast code as is necessary. Generally, you do not specialize broadcast
, but rather broadcast_c
.
I have a quick demo of what broadcast_c
means:
julia> struct Poison end
julia> Base.Broadcast._containertype(::Type{<:Poison}) = Poison
julia> Base.Broadcast.promote_containertype(::Type{Poison}, _) = Poison
julia> Base.Broadcast.promote_containertype(_, ::Type{Poison}) = Poison
julia> Base.Broadcast.promote_containertype(::Type{Poison}, ::Type{Array}) = Poison
julia> Base.Broadcast.promote_containertype(::Type{Array}, ::Type{Poison}) = Poison
julia> Base.Broadcast.broadcast_c(f, ::Type{Poison}, _...) = "hijacked broadcasting"
julia> Poison() .+ [1, 2, 3]
"hijacked broadcasting"
- roughly, the way
broadcast
works is: it looks at the argument types it gets and tries to determine whichbroadcast_c
method handles broadcasting of those types. - first it calls
_containertype
on the types of all arguments - then it calls
promote_containertype
on all of the return values of_containertype
So the part to ensure that Broadcast
will dispatch to your desired broadcast_c
method is:
struct Poison end
Base.Broadcast._containertype(::Type{<:Poison}) = Poison
Base.Broadcast.promote_containertype(::Type{Poison}, _) = Poison
Base.Broadcast.promote_containertype(_, ::Type{Poison}) = Poison
and the part actually defining the broadcast behavior is:
Base.Broadcast.broadcast_c(f, ::Type{Poison}, _...) = "hijacked broadcasting"
Why do we need to treat arrays separately? In this case, because of ambiguities. The broadcast code in Base
itself has the equivalent of
promote_containertype(::Type{Array}, _) = Array
promote_containertype(_, ::Type{Array}) = Array
so there will be ambiguities unless you treat arrays with their own extra methods.
Note broadcast
dispatches to broadcast_c
and broadcast!(f, ::AbstractArray, ...)
dispatches to broadcast_c!
in roughly the same way. Note that broadcast!
by default is only defined for the LHS an AbstractArray
. If you have your own type on the LHS you will need to specialize broadcast!
directly instead of broadcast_c!
(or perhaps in addition to, if you’d like to call broadcast_c!).
So in summary:
- Do not add methods to
broadcast
. - Instead, add methods to
broadcast_c
, which has the same signature ofbroadcast
except an additional argument is added after the function argument, which will be a::Type{...}
of your choosing. - Then, add methods to
_containertype
andpromote_containertype
so that the genericbroadcast
can pick the correct method. - The same applies for
broadcast!
andbroadcast_c!
, except that when the LHS is not anAbstractArray
, additional methods may be required.