I am trying to understand the full usage of similar(). The manual shows an example
similar(falses(10), Float64, 2, 4)
and states that
Since BitArray s can only store elements of type Bool, however, if you request a different element type it will create a regular Array instead
Since the type and the dimension of the matrix (or array in general) to be created are specified, I am wondering what is the point in using the above code and not writing just
Matrix{Float64}(undef, 2, 4)
instead.
Maybe it it is just plurality of ways to express the same thing, but I would be grateful for clarification. Namely does the full usage of similar, that is, specifying all the three input parameters (array, type, size), give any advantage over the latter way of creating an unitialized matrix of a given type and size?
The point is that similar allows the “reference” array to have some sort of say in what it returns. The example is perhaps overly simplistic — the power of similar comes when you don’t immediately know one of the arguments and need to allocate a scratch or result array that is kinda-sorta like the reference one. For example, a simple map could be written as:
function map(f, A)
v1 = f(first(A))
result = similar(A, typeof(v1))
for i in eachindex(A)
result[i] = f(A[i])
end
return result
end
This is overly simplistic (it evaluates f twice for the first element, doesn’t support widening if the types change, etc, etc), but I hope you can see how it will allow the original array to have some sort of say in what a good array might be for result. For example, if A is a BitArray, it will either be a BitArray (if f also returns Bools) or a Array otherwise. Or if A is a static array, it’ll return an array that preserves its knowledge of the array size. Or if A is an immutable array (like 1:10) then it’ll always give you an Array since ranges can’t be modified at all.
The same applies if you give it all three arguments — one or more of them might not be known to you ahead of time but by passing them all to similar, you can write generic code that does something reasonable through the power of multiple dispatch.
Another way of putting this is that if you know you want a Matrix{Float64} of the given size, then using the Matrix{Float64}(undef, 2, 4) constructor is the right thing to do. You might want to use similar() when you don’t know exactly what kind of array-like thing you want, and you want the type of the arguments passed to similar to have some effect on what the resulting type is.
If you know all the inputs to similar beforehand, then it’s not much point in using it.
But I think it would be awkward to disallow specifying all of them while allowing every-which-but-one. It would be surprising and seem inconsistent to the user, I think, and potentially awkward to implement (maybe one could just throw an error if fully specified—but why!?).