Skip to content

Commit c4e8f44

Browse files
committed
add new abstract type for iterators with HasShape: ShapefulIterator
Iterators with `HasShape{N}` are certainly the most well-behaved class of iterators in Julia. Currently several separate type trees of such iterators come with Julia. It seems like joining these trees under a single abstract type would be nice. Why this change is useful: * Make expressing hardcoded `Union` types for zero-dimensional collections unnecessary: instead of `Union{Number,Ref,AbstractArray{<:Any,0}}` now it will be possible to write simply, and more generically, `ShapefulIterator{0}`. At least in some cases. * Reduce the number of `ndims` and `IteratorSize` methods: * The number of `ndims` methods that come with Julia is decreased by six * The number of `IteratorSize` methods that come with Julia is decreased by three Why this change doesn't prevent a better future supertype choice: * Naively it might make sense to have `Number`, `AbstractChar` and `Ref` subtype a parameterless abstract type like `ZeroDimensionalIterator`. However: * This seems less useful than `ShapefulIterator{N}` * Adding `ShapefulIterator` doesn't preclude adding `ZeroDimensionalIterator <: ShapefulIterator{0}` later. * One might ask: why not make `AbstractArray{T}` subtype a different abstract type, with an element type type parameter, something like `AbstractArray{T,N} <: TypedIterator{T}`. In this case, however, `TypedIterator` would be useless for the zero dimensional iterators mentioned above, as they are their own element types, something not expressible in abstract type subtyping.
1 parent 0cf5a4d commit c4e8f44

File tree

19 files changed

+78
-47
lines changed

19 files changed

+78
-47
lines changed

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ Build system changes
2424
New library functions
2525
---------------------
2626

27+
* a new abstract type for iterators with `HasShape{N}` is added: `ShapefulIterator{N}`. It supertypes existing abstract types:
28+
* `AbstractArray`
29+
* `AbstractChar`
30+
* `Number`
31+
* `Ref`
32+
It's still acceptable to define a new shapeful iterator type that does not subtype `ShapefulIterator`, however choosing to subtype `ShapefulIterator{N}` means there's no need to add certain methods for one's newly-defined type.
33+
2734
New library features
2835
--------------------
2936

base/abstractarray.jl

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
## Basic functions ##
44

55
"""
6-
AbstractArray{T,N}
6+
AbstractArray{T,N} <: ShapefulIterator{N}
77
88
Supertype for `N`-dimensional arrays (or array-like types) with elements of type `T`.
99
[`Array`](@ref) and other types are subtypes of this. See the manual section on the
@@ -256,25 +256,6 @@ julia> Base.elsize(rand(Float32, 10))
256256
"""
257257
elsize(A::AbstractArray) = elsize(typeof(A))
258258

259-
"""
260-
ndims(A::AbstractArray)::Integer
261-
262-
Return the number of dimensions of `A`.
263-
264-
See also: [`size`](@ref), [`axes`](@ref).
265-
266-
# Examples
267-
```jldoctest
268-
julia> A = fill(1, (3,4,5));
269-
270-
julia> ndims(A)
271-
3
272-
```
273-
"""
274-
ndims(::AbstractArray{T,N}) where {T,N} = N::Int
275-
ndims(::Type{<:AbstractArray{<:Any,N}}) where {N} = N::Int
276-
ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements"))
277-
278259
"""
279260
length(collection)::Integer
280261

base/boot.jl

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
#end
5050
#const nothing = Nothing()
5151

52-
#abstract type AbstractArray{T,N} end
52+
#abstract type ShapefulIterator{N} end
53+
#abstract type AbstractArray{T,N} <: ShapefulIterator{N} end
5354
#abstract type DenseArray{T,N} <: AbstractArray{T,N} end
5455

5556
#primitive type AddrSpace{Backend::Module} 8 end
@@ -208,7 +209,7 @@ export
208209
# key types
209210
Any, DataType, Vararg, NTuple,
210211
Tuple, Type, UnionAll, TypeVar, Union, Nothing, Cvoid,
211-
AbstractArray, DenseArray, NamedTuple, Pair,
212+
AbstractArray, DenseArray, NamedTuple, Pair, ShapefulIterator,
212213
# special objects
213214
Function, Method, Module, Symbol, Task, UndefInitializer, undef, WeakRef, VecElement,
214215
Array, Memory, MemoryRef, AtomicMemory, AtomicMemoryRef, GenericMemory, GenericMemoryRef,
@@ -245,7 +246,7 @@ export
245246
const getproperty = getfield # TODO: use `getglobal` for modules instead
246247
const setproperty! = setfield!
247248

248-
abstract type Number end
249+
abstract type Number <: ShapefulIterator{0} end
249250
abstract type Real <: Number end
250251
abstract type AbstractFloat <: Real end
251252
abstract type Integer <: Real end
@@ -259,7 +260,7 @@ primitive type Float64 <: AbstractFloat 64 end
259260
primitive type BFloat16 <: AbstractFloat 16 end
260261

261262
#primitive type Bool <: Integer 8 end
262-
abstract type AbstractChar end
263+
abstract type AbstractChar <: ShapefulIterator{0} end
263264
primitive type Char <: AbstractChar 32 end
264265

265266
primitive type Int8 <: Signed 8 end

base/broadcast.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ to_index(Is::Tuple) = CartesianIndex(Is)
643643
644644
Index into `A` with `I`, collapsing broadcasted indices to their singleton indices as appropriate.
645645
"""
646-
Base.@propagate_inbounds _broadcast_getindex(A::Union{Ref,AbstractArray{<:Any,0},Number}, I) = A[] # Scalar-likes can just ignore all indices
646+
Base.@propagate_inbounds _broadcast_getindex(A::ShapefulIterator{0}, I) = A[] # Scalar-likes can just ignore all indices
647647
Base.@propagate_inbounds _broadcast_getindex(::Ref{Type{T}}, I) where {T} = T
648648
# Tuples are statically known to be singleton or vector-like
649649
Base.@propagate_inbounds _broadcast_getindex(A::Tuple{Any}, I) = A[1]
@@ -728,7 +728,7 @@ Base.RefValue{String}("hello")
728728
"""
729729
broadcastable(x::Union{Symbol,AbstractString,Function,UndefInitializer,Nothing,RoundingMode,Missing,Val,Ptr,AbstractPattern,Pair,IO,CartesianIndex}) = Ref(x)
730730
broadcastable(::Type{T}) where {T} = Ref{Type{T}}(T)
731-
broadcastable(x::Union{AbstractArray,Number,AbstractChar,Ref,Tuple,Broadcasted}) = x
731+
broadcastable(x::Union{ShapefulIterator,Tuple,Broadcasted}) = x
732732
# Default to collecting iterables — which will error for non-iterables
733733
broadcastable(x) = collect(x)
734734
broadcastable(::Union{AbstractDict, NamedTuple}) = throw(ArgumentError("broadcasting over dictionaries and `NamedTuple`s is reserved"))

base/char.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import Core: AbstractChar, Char
44

55
"""
6+
AbstractChar <: ShapefulIterator{0}
7+
68
The `AbstractChar` type is the supertype of all character implementations
79
in Julia. A character represents a Unicode code point, and can be converted
810
to an integer via the [`codepoint`](@ref) function in order to obtain the
@@ -202,10 +204,7 @@ typemin(::Type{Char}) = bitcast(Char, typemin(UInt32))
202204

203205
size(c::AbstractChar) = ()
204206
size(c::AbstractChar, d::Integer) = d < 1 ? throw(BoundsError()) : 1
205-
ndims(c::AbstractChar) = 0
206-
ndims(::Type{<:AbstractChar}) = 0
207207
length(c::AbstractChar) = 1
208-
IteratorSize(::Type{Char}) = HasShape{0}()
209208
firstindex(c::AbstractChar) = 1
210209
lastindex(c::AbstractChar) = 1
211210
getindex(c::AbstractChar) = c

base/docs/basedocs.jl

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,10 @@ which are their descendants. Abstract types form the conceptual hierarchy which
115115
Julia’s type system more than just a collection of object implementations. For example:
116116
117117
```julia
118-
abstract type Number end
118+
abstract type ShapefulIterator{N} end
119119
abstract type Real <: Number end
120120
```
121-
[`Number`](@ref) has no supertype, whereas [`Real`](@ref) is an abstract subtype of `Number`.
121+
[`ShapefulIterator`](@ref) has no supertype, whereas [`Real`](@ref) is an abstract subtype of `Number`.
122122
"""
123123
kw"abstract type", kw"abstract"
124124

@@ -2184,7 +2184,23 @@ Stacktrace:
21842184
DivideError
21852185

21862186
"""
2187-
Number
2187+
ShapefulIterator{N}
2188+
2189+
`N`-dimensional [iterator](@ref man-interface-iteration) type, where `N isa Int`.
2190+
2191+
These functions have methods defined for all subtypes of `ShapefulIterator`, no need to add new methods - if you do add new methods they must be consistent with the following definitions:
2192+
* [`ndims`](@ref): returns `N`
2193+
* [`Base.IteratorSize`](@ref): returns `Base.HasShape{N}()`
2194+
2195+
New subtypes must implement:
2196+
* [`length`](@ref)
2197+
* [`size`](@ref)
2198+
* [`axes`](@ref), if the new subtype uses non-traditional indices
2199+
"""
2200+
ShapefulIterator
2201+
2202+
"""
2203+
Number <: ShapefulIterator{0}
21882204
21892205
Abstract supertype for all number types.
21902206
"""

base/exports.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export Core,
55
# key types
66
Any, DataType, Vararg, NTuple,
77
Tuple, Type, UnionAll, TypeVar, Union, Nothing, Cvoid,
8-
AbstractArray, DenseArray, NamedTuple, Pair,
8+
AbstractArray, DenseArray, NamedTuple, Pair, ShapefulIterator,
99
# special objects
1010
Function, Method, Module, Symbol, Task, UndefInitializer, undef, WeakRef, VecElement,
1111
Array, Memory, MemoryRef, AtomicMemory, AtomicMemoryRef, GenericMemory, GenericMemoryRef,

base/generator.jl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,30 @@ IteratorSize(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not
9797
IteratorSize(::Type{Any}) = SizeUnknown()
9898

9999
IteratorSize(::Type{<:Tuple}) = HasLength()
100-
IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}()
100+
IteratorSize(::Type{<:ShapefulIterator{N}}) where {N} = HasShape{N::Int}()
101101
IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I)
102102

103103
haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength}
104104

105+
"""
106+
ndims(A::ShapefulIterator)::Int
107+
108+
Return the number of dimensions of `A`.
109+
110+
See also: [`size`](@ref), [`axes`](@ref).
111+
112+
# Examples
113+
```jldoctest
114+
julia> A = fill(1, (3,4,5));
115+
116+
julia> ndims(A)
117+
3
118+
```
119+
"""
120+
ndims(::ShapefulIterator{N}) where {N} = N::Int
121+
ndims(::Type{<:ShapefulIterator{N}}) where {N} = N::Int
122+
ndims(::Type{Union{}}, slurp...) = throw(ArgumentError("Union{} does not have elements"))
123+
105124
abstract type IteratorEltype end
106125
struct EltypeUnknown <: IteratorEltype end
107126
struct HasEltype <: IteratorEltype end

base/number.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,14 +82,11 @@ size(x::Number, d::Integer) = d < 1 ? throw(BoundsError()) : 1
8282
axes(x::Number) = ()
8383
axes(x::Number, d::Integer) = d < 1 ? throw(BoundsError()) : OneTo(1)
8484
eltype(::Type{T}) where {T<:Number} = T
85-
ndims(x::Number) = 0
86-
ndims(::Type{<:Number}) = 0
8785
length(x::Number) = 1
8886
firstindex(x::Number) = 1
8987
firstindex(x::Number, d::Int) = d < 1 ? throw(BoundsError()) : 1
9088
lastindex(x::Number) = 1
9189
lastindex(x::Number, d::Int) = d < 1 ? throw(BoundsError()) : 1
92-
IteratorSize(::Type{<:Number}) = HasShape{0}()
9390
keys(::Number) = OneTo(1)
9491

9592
getindex(x::Number) = x

base/refpointer.jl

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import Core: Ref
44

55
"""
6-
Ref{T}
6+
Ref{T} <: ShapefulIterator{0}
77
88
An object that safely references data of type `T`. This type is guaranteed to point to
99
valid, Julia-allocated memory of the correct type. The underlying data is protected from
@@ -100,11 +100,8 @@ size(x::Ref) = ()
100100
axes(x::Ref) = ()
101101
length(x::Ref) = 1
102102
isempty(x::Ref) = false
103-
ndims(x::Ref) = 0
104-
ndims(::Type{<:Ref}) = 0
105103
iterate(r::Ref) = (r[], nothing)
106104
iterate(r::Ref, s) = nothing
107-
IteratorSize(::Type{<:Ref}) = HasShape{0}()
108105

109106
# create Ref objects for general object conversion
110107
unsafe_convert(::Type{Ref{T}}, x::Ref{T}) where {T} = unsafe_convert(Ptr{T}, x)

0 commit comments

Comments
 (0)