`.*` falls back to `Base.broadcast` only for identical arguments

Here’s a weird one:

struct Testype
    x::Int64
end

import Base.broadcast

Base.broadcast(::typeof(*), t1::Testype, t2::Testype) = (t1.x * t2.x)

t1 = Testype(2)
t2 = Testype(3)

Now, if you do t1 .* t2 everything is fine. However, if you do t1 .* t1 you get

ERROR: MethodError: no method matching *(::Testype, ::Testype)
Closest candidates are:
  *(::Any, ::Any, ::Any, ::Any...) at operators.jl:424
Stacktrace:
 [1] (::##3#4)(::Testype) at ./<missing>:0
 [2] broadcast(::Function, ::Testype) at ./broadcast.jl:434

It appears that the call to broadcast(*, ...) falls back to the Base definition only in cases where a repeated argument is given.

Why? How do I circumvent this?

I suppose this might be some special behavior to handle squaring more efficiently, but it’s not exactly transparent to the user how this is being done or what methods he should extend to fix it.

Thanks all.

https://github.com/JuliaLang/julia/issues/22053

Thanks. I’m starting to get the sense that people should not overload broadcast(::typeof(op), args...) except in cases where there are explicit loops over * (and, in such cases, it’s hard to see why you’d want to).

It seems that the way things are going, if a package implements a Hadamard product or something similar but said product does not involve a loop over *, the package should instead use a different operator symbol such as .

This (issue, PR) should address the issue, right? Eventually we want MXNet.jl and friends to actually make use of broadcast fusion, but for now the underlying library (mshadow) only exposes individual vectorized ops and we should essentially inform the Julia compiler of that limitation through something like this isfusing trait. In the case of MXNet/mshadow, that’s enough; I guess it’s possible that another library (or a future improved mshadow) could expose a limited version of broadcast fusion (perhaps only for certain operators) and Julia would have to figure out how to support that without applying fusion in cases where the library can’t understand it…