Wow, thanks for this very detailed post! Lots to unpack and to digest! (for a future day with more time)
I have a few direct reactions:
I like how the example illustrates that code written for, e.g. some numerical computation, should work well with both value types and reference types. I had not really though about this.
I fully agree that a default instance/value is a bad idea.
You compare the cost of initialization when going from “no initialization” to “initialization”, of course this can look bad (yes, I know “no initialization” requires “null initialization”, but still). Luckily, I think, initialization is usually not the dominant cost in a computation - and if it is, then that’s a pretty good sign you are using a dense structure when you should be using a sparse.
The Pascal example connects back to earlier discussion in this thread about incremental construction and sizehint!
function pascal(::Type{T}, n::Integer) where {T<:Real}
A = T[]
sizehint!(A, n*n)
idx(i, j) = n * (j - 1) + i
for j = 1:n
for i = 1:n
if i == 1 || j == 1
push!(A, one(T))
else
push!(A, A[idx(i-1, j)] + A[idx(i, j-1)])
end
end
end
return reshape(A, (n,n))
end
Yes, your index-style implementation looks much better than this one.