What is the future of `sin(::Vector)` when there is `sin.(::Vector)`?

Yes, that’s the issue though. As pointed out, your solution of:

function broadcast(f::typeof(sin),x::Vector)
# do fast broadcast
end

doesn’t necessarily work because, if I understand correctly, when broadcasts are fused like in y .= sin.(x) + cos.(x), the f that is called is an anonymous function f = (x)->sin(x)+cos(x) and so writing an override for out .= sin.([1 3 4]) doesn’t easily generalize to cases where broadcast fusing will take place.

Here’s a way of looking at it. When you don’t have optimized vector forms, on the scalar form what you’d want to do is:

f = (x) -> sin(x) + cos(x)
y .= f.(x)

But you can also avoid temporaries by using non-allocating sin! and cos!, and then add them. (But this gets difficult because you need a cache). So in the case where you want to use optimized vector functions, you’d want to do something like:

sin!(cache1,x)
cos!(cache2,x)
y.= cache1 .+ cache2

Somehow we want something like y .= sin.(x) + cos.(x) to mean both of these, depending on whether a “fast vectorized form” exists. I am not sure that this could be done automatically (since how else could caches be specified?), so I am suggesting that

y .= sin!(cache1,x) .+ cos!(cache2,x)

would have to be the way to go. The RNG already do something like this so there’s precedent. So I would suggest that, since any optimized vector form is for performance, the only form that should really need to be present is the mutating version. It would be easy to document too because it would be a different function. At least placeholders for common functions like sin! should be exported from Base so that way libraries like GPU libraries could extend it. I think that’s still pretty clean and very Julia syntax.