That makes some sense.
You consider introducing an intermediary abstract array type, say, AbstractSimpleArray
which always begins with index 1 and indices are separated by one(T) [or oneunit(T) for Ints they are the same].
What you are constraining is less the Array abstraction than its indices convention / constraint / requirements. There is an AbstractUnitRange{T}
which is
Supertype for ranges with a step size of oneunit(T) with elements of type T. UnitRange and other types are subtypes of this.
and a concrete type Base.OneTo
:
help? > Base.OneTo
Define an AbstractUnitRange that behaves like 1:n, with the added
distinction that the lower limit is guaranteed (by the type system) to
be 1.
The AbstractSimpleArray
is an AbstractArray with indices that conform to Base.OneTo
. Of course, one may want a 1 based array that takes 2 or n steps from index to index – however that is no longer simple. And there are subtypes of AbstractArray{T,N} that would need to remain as subtypes(AbstractArray) and also to be sub-specialized with unique names as subtypes(AbstractSimpleArray) or Better it seems, with a range categorization constraining parameter
[though this need not be breaking, great care is needed to insure that].
Here is a direction, without much consideration for what-would-happen.
There is a better refactoring than using Range types as params.
abstract type AbstractArray{T,N} end
abstract type AbstractSteppedArray{T,N,R} <: AbstractArray{T,N} end
abstract type AbstractSimpleArray{T,N} <: AbstractSteppedArray{T,N,Base.OneTo} end
struct SimpleArray{T,N} <: AbstractSimpleArray{T,N}
values::Array{T,N}
end
Base.size(x::AbstractSimpleArray) =
isempty(x.values) ? nothing : size(x.values)
Base.get(x::SimpleArray{T,1}, i::Integer, default::T=zero(T)) where {T<:Number} =
getindex(x.values, i, default)
Base.get(x::SimpleArray{T,1}, i::Integer, default::T) where {T} =
getindex(x.values, i, default)
Base.getindex(x::SimpleArray{T,1}, i::Integer) where {T<:Number} =
1 <= i <= length(x.values) ? getindex(x.values, i) : nothing
# dispatch catches disallowed range[s] if used to index
# ...
additional info from the docs:
If a type is defined as a subtype of AbstractArray
, it inherits a very large set of rich behaviors including iteration and multidimensional indexing built on top of single-element access. See the arrays manual page and the Julia Base section for more supported methods.
A key part in defining an AbstractArray
subtype is IndexStyle
. Since indexing is such an important part of an array and often occurs in hot loops, it’s important to make both indexing and indexed assignment as efficient as possible. Array data structures are typically defined in one of two ways: either it most efficiently accesses its elements using just one index (linear indexing) or it intrinsically accesses the elements with indices specified for every dimension. These two modalities are identified by Julia as IndexLinear()
and IndexCartesian()
. Converting a linear index to multiple indexing subscripts is typically very expensive, so this provides a traits-based mechanism to enable efficient generic code for all array types.
This distinction determines which scalar indexing methods the type must define. IndexLinear()
arrays are simple: just define getindex(A::ArrayType, i::Int)
.