I wonder if there are any guidelines on the specificity of method arguments.
I’m thinking about the trade-offs of making a type very general (e.g., ::Any
(or omitted), ::AbstractArray
) vs making a type more or less specific (e.g., ::AbstractArray{Float64}
or ::AbstractArray{<:Number}
).
These are the trade-offs I understood from the official style guide and the official performance considerations:
- Pro more concrete types:
- enables (future) dispatching
- more ‘confidence’ in type stability (fewer possible input types for which to check type stability)
- allow user to convert to required format (e.g., conversion to int)
- serves as documentation
- helps to ensure program correctness at compile-time.
- Pro more general types:
- works for more use-cases
- works for non-standard types with additional functionality (e.g., numbers with uncertainty, dual numbers,
CartesianIndex
instead ofInt
,BitVector
instead ofVector{Bool}
)
Are there any guidelines or rules-of-thumb on this topic?
Coming mainly from C++, I feel a little uneasy when I see completely unconstrained types for complex functions personally. Things like foo(x) = sqrt(x - 22*oneunit(x))
are completely fine, but when the function has more than two or three lines or when x is used as an iterable or as an array, completely unspecified argument types terrify me a little bit.
On the other hand, I’d assume, one should aim to make types as wide as possible, ideally. What are the ‘soft’ reasons to constrain a type besides the typical ‘hard’ api reasons like “I need dispatch”?
As an example, how would I want to constrain a collection of indices?
::AbstractArray{<:Signed}
prevents the use with CartesianIndex
. An abstract array of a union is a lot of noise to type and read. I guess, I could just use an unconstrained argument and let the compiler figure out, if the argument is indexable or iterable (depending on the usage) and if the argument’s elements can be used as indices themselves. But that would make the function require additional documentation. Also, I’d fear that I’m missing some edge case, for which the function compiles and runs, but does utter nonsense as the argument does not behave like a collection of indices.