I often find myself writing two methods for a simple scalar function. E.g.

function gauss(x::Real; a=1.0, c=0.0, s=0.7)
return a/s/(sqrt(2pi))*exp(-((x-c)^2/(2*s^2)))
end
function gauss(x::AbstractArray{<:Real}; a=1.0, c=0.0, s=0.7)
return [gauss(xx, a=a, c=c, s=s) for xx in x]
end
xs = -5:0.01:5
ys = gauss(xs)

Could this be reduced to a single function definition that dispatches on single scalar types and arrays of reals?

Could you use broadcasting ys = gauss.(xs)? That’d be the most common idiomatic way to make scalar functions work on each element of an array. Otherwise, I think you’d need to make extra forwarding methods like so.

The point being: don’t make a special version for arrays at all (not even one that uses broadcasting internally), only make the scalar version, and call it with dot broadcasting.

Exception: an array, likely with a static size, is the indivisible data point, in which case you’d make a function working on them and could broadcast it over arrays of such arrays.

Oh, sure, functions that are inherently meant to take arrays as a fundamental working unit (like e.g. linear algebra, too). But functions that are inherently scalar ‘by nature’ should just be defined scalars, and broadcasted at the call site.

Another possible exception is if there are common calculations that can be avoided by specializing, or other significant optimization opportunities.

Conflating 1-element vectors and scalars is tempting, and useful in many cases, but keeping stricter separation promotes clarity and may circumvent some bugs through static analysis and better design. The latter seems to weigh heavier in most cases.