I agree with the general thrust of your argument, but my impression is that the reason this and the other thread have gone on for so long is that the terms “generic,” “weird,” and “just work” admit a lot of room for ambiguity. It is evident that many people consider a 0-indexed array implemented via OffsetArrays.jl to be an essential part of their workflow, whereas for others it’s a weird choice given that Julia is a 1-based language.
Yep, it’s all quite subjective. What I was pointing at is rather: if the code is for your personal use, just make any assumptions you want and live a relaxed and happy life. Otherwise, of course, try to be fully generic. This makes for two very different coding experiences, the first casual, easy but non-scalable and meh; the second intellectually much more challenging and beautiful.
But I also have a healthy awareness of the fact that I am not an expert coder, and (in cases where it provides a performance advantage) I would like to be able to use things like @inbounds
with impunity. With an Array
, I know what “contract” I am signing and where the edge cases are; with an AbstractArray
, I’m not sure I do (yet). And to reiterate, the concern isn’t merely that someone will pass an MyFunnyAbstractArray
and get an out-of-bounds error—the concern is that the code will be silently incorrect and/or leak memory.
100% agreed, Array
means a whole range of things beyond a specific API. You know what implementation it uses, and this information is very often crucial.
This is the challenge we all face with generic coding to higher or lesser degree, regardless of our expertise, I think: limited information. When we write ::AbstractArray
we are usually not fully sure what we’re signing up for.
Ok, if you have lots of experience you know the interface by heart, and you know what things you can or cannot do with that variable to have correct code, so your code will likely work quite widely after some tinkering. Invariably, you will need to do tests with a range of unusual inputs to be sure. But one thing I often struggle with when I cannot assume anything about internal implementation is performance. Even if you respect the interface you cannot be sure your implementation will be optimal. For example, if you get a sparse array, you better be careful to avoid setindex!
. That can become a huge performance footgun. Even getindex
can be much slower than necessary, so you would need to structure your code to take a specialized iteration approach if you happen to have a sparse input. Multiple dispatch is great for this, but this effort takes you into a process of specialization and iteration that often makes your code less and less beautiful and transparent.
So what would Julia 2.0 need to allow us to navigate generic coding with more certainty and aplomb? I’d say we need a way to express the implicit assumptions in our mind about types (regarding API but also performance). So, that means a full-on trait system. But in any case that would still be only part of the solution because you first need to become aware of the assumptions you are making (which is why you seldom see Base.require_one_based_indexing(a)
preceding for i in 1:length(a)
in the ecosystem).