From c4707a1e8662849ffff721ed07d25e27853131c4 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 5 Jul 2017 12:42:39 -0400 Subject: [PATCH 1/2] tuple type introspection via indexing/iteration --- NEWS.md | 3 +++ base/tuple.jl | 20 ++++++++++++++++++++ doc/src/manual/types.md | 22 ++++++++++++++++++++++ test/tuple.jl | 22 ++++++++++++++++++++++ 4 files changed, 67 insertions(+) diff --git a/NEWS.md b/NEWS.md index cd741cb50ab56..7f2fae8117ef3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -49,6 +49,9 @@ This section lists changes that do not have deprecation warnings. Library improvements -------------------- + * The components of a tuple type `T` may now be iterated over and indexed + as if `T` were a tuple of types. + * The functions `strip`, `lstrip` and `rstrip` now return `SubString` ([#22496]). * The functions `base` and `digits` digits now accept a negative diff --git a/base/tuple.jl b/base/tuple.jl index f78502525a839..ef9605ef04a22 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -310,3 +310,23 @@ any(x::Tuple{}) = false any(x::Tuple{Bool}) = x[1] any(x::Tuple{Bool, Bool}) = x[1]|x[2] any(x::Tuple{Bool, Bool, Bool}) = x[1]|x[2]|x[3] + +## tuple-type introspection as array/iterator ## +# ... we can't define iteratorsize/iteratoreltype, however, because +# typeof(Tuple) is simply DataType +_iteratorsize(::Type{T}) where {T<:Tuple} = (@_pure_meta; isvatuple(T) ? IsInfinite() : HasLength()) +_length(::HasLength, T::Type{<:Tuple}) = length(T.types) +_length(::IsInfinite, T::Type{<:Tuple}) = typemax(Int) +length(T::Type{<:Tuple}) = (@_inline_meta; _length(_iteratorsize(T), T)) +endof(T::Type{<:Tuple}) = (@_inline_meta; length(T)) +getindex(::Type{T}, i::Integer) where {T<:Tuple} = _getindex(_iteratorsize(T), T, i) +_getindex(::HasLength, T::Type{<:Tuple}, i::Integer) = (@_propagate_inbounds_meta; T.types[i]) +function _getindex(::IsInfinite, T::Type{<:Tuple}, i::Integer) + @_propagate_inbounds_meta + return i ≥ length(T.types) ? unwrapva(T.types[end]) : T.types[i] +end +start(::Type{<:Tuple}) = 1 +next(T::Type{<:Tuple}, i::Int) = (@_propagate_inbounds_meta; (T[i],i+1)) +done(T::Type{<:Tuple}, i::Int) = (@_inline_meta; i == length(T)+1) +first(T::Type{<:Tuple}) = T[1] +last(T::Type{<:Tuple}) = T[end] diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 281070a7e8d77..652db8ba29d4f 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -877,6 +877,25 @@ false Intuitively, this corresponds to the type of a function's arguments being a subtype of the function's signature (when the signature matches). +You can inspect the component types of a tuple type `T` almost +as if `T` were an array or tuple of types: +```jldoctest +julia> T = Tuple{Int8,String,Float32} +Tuple{Int8,String,Float32} + +julia> length(T) +3 + +julia> T[2] +String + +julia> collect(Type, T) +3-element Array{Type,1}: + Int8 + String + Float32 +``` + ### Vararg Tuple Types The last parameter of a tuple type can be the special type `Vararg`, which denotes any number @@ -905,6 +924,9 @@ used to represent the arguments accepted by varargs methods (see [Varargs Functi The type `Vararg{T,N}` corresponds to exactly `N` elements of type `T`. `NTuple{N,T}` is a convenient alias for `Tuple{Vararg{T,N}}`, i.e. a tuple type containing exactly `N` elements of type `T`. +For introspection/indexing purposes, a `Vararg` tuple type behaves like an "array" of +an unbounded number of types (its [`length`](@ref) is `typemax(Int)`). + #### [Singleton Types](@id man-singleton-types) There is a special kind of abstract parametric type that must be mentioned here: singleton types. diff --git a/test/tuple.jl b/test/tuple.jl index 925cb9dfa0357..0af9be8c332cd 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -291,3 +291,25 @@ let test_15703() end + +@testset "tuple type introspection" begin + for (T,a) in ((Tuple{}, Type[]), + (Tuple{Int,String,Int8}, [Int,String,Int8]), + (NTuple{4,Int}, [Int,Int,Int,Int]), + (Tuple{Int,String,Vararg{Int8}}, [Int,String,Int8,Int8,Int8])) + @test isempty(T) == isempty(a) + if Base._iteratorsize(T) isa Base.HasLength + @test length(T) == endof(T) == length(a) + @test collect(Type, T) == a == [T[i] for i = 1:length(T)] + @test_throws BoundsError T[length(T)+1] + else + @test length(T) == endof(T) == typemax(Int) + end + if !isempty(T) + @test first(T) == first(a) + @test last(T) == last(a) == T[end] + end + @test collect(Type, Base.Iterators.take(T, length(a))) == a == [T[i] for i = 1:length(a)] + @test_throws BoundsError T[0] + end +end From d95f85fa91be4da01676b01e3d5df0110c28be6f Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 5 Jul 2017 12:50:21 -0400 Subject: [PATCH 2/2] add PR number to NEWS --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 7f2fae8117ef3..7a2287a3ffa95 100644 --- a/NEWS.md +++ b/NEWS.md @@ -50,7 +50,7 @@ Library improvements -------------------- * The components of a tuple type `T` may now be iterated over and indexed - as if `T` were a tuple of types. + as if `T` were a tuple of types ([#22687]). * The functions `strip`, `lstrip` and `rstrip` now return `SubString` ([#22496]).