Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tuple type introspection via indexing/iteration #22687

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 ([#22687]).

* The functions `strip`, `lstrip` and `rstrip` now return `SubString` ([#22496]).

* The functions `base` and `digits` digits now accept a negative
Expand Down
20 changes: 20 additions & 0 deletions base/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

little odd for something that's supposed to be infinite to return a finite length?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it throw an error instead? (I could just delete this line and it would throw a MethodError in that case.)

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, length usually gives a MethodError on infinite iterators.

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]
22 changes: 22 additions & 0 deletions doc/src/manual/types.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
22 changes: 22 additions & 0 deletions test/tuple.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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