-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
HasLength trait for some Flatten iterators #22691
Conversation
base/iterators.jl
Outdated
iteratoreltype(::Type{Flatten{I}}) where {I} = _flatteneltype(I, iteratoreltype(I)) | ||
_flatteneltype(I, ::HasEltype) = iteratoreltype(eltype(I)) | ||
_flatteneltype(I, et) = EltypeUnknown() | ||
|
||
flatten_iteratorsize(::Union{HasShape, HasLength}, b::Type{T}) where {T<:Tuple} = HasLength() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Split lines at 92 characters. Here, you can do ::Type{<:Tuple}
to save some space.
base/iterators.jl
Outdated
flatten_iteratorsize(::Union{HasShape, HasLength}, b::Type{T}) where {T<:Tuple} = HasLength() | ||
flatten_iteratorsize(::Union{HasShape, HasLength}, b::Type{T}) where {T<:Number} = HasLength() | ||
flatten_iteratorsize(a, b) = SizeUnknown() | ||
flatten_length(f, ::Type{T}) where {T<:Tuple} = isleaftype(T) ? nfields(T)*length(f.it) : throw(ArgumentError("Cannot compute length of a tuple-type which is not a leaf-type")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line length here as well. Perhaps write this method definition long-form with an if
-else
?
base/iterators.jl
Outdated
flatten_length(f, ::Type{T}) where {T<:Tuple} = isleaftype(T) ? nfields(T)*length(f.it) : throw(ArgumentError("Cannot compute length of a tuple-type which is not a leaf-type")) | ||
flatten_length(f, ::Type{T}) where {T<:Number} = length(f.it) | ||
length(f::Flatten{I}) where {I} = flatten_length(f, eltype(I)) | ||
iteratorsize(::Type{Flatten{I}}) where {I} = isleaftype(eltype(I)) ? flatten_iteratorsize(iteratorsize(I), eltype(I)) : SizeUnknown() # eltype is always defined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise here re. line length. Perhaps also better as a long-form method definition with an if
-else
? :)
test/iterators.jl
Outdated
@@ -347,7 +347,9 @@ end | |||
@test collect(flatten(Any[flatten(Any[1:2, 6:5]), flatten(Any[6:7, 8:9])])) == Any[1,2,6,7,8,9] | |||
@test collect(flatten(Any[2:1])) == Any[] | |||
@test eltype(flatten(UnitRange{Int8}[1:2, 3:4])) == Int8 | |||
@test length(flatten(zip(1:3,4:6))) == 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A space between the arguments to zip
would aid visual parsing :).
test/iterators.jl
Outdated
@test_throws ArgumentError collect(flatten(Any[])) | ||
@test_throws ArgumentError length(flatten(NTuple[(1,),()])) # #16680 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Likewise here, a space following the comma between Tuple
s might make this easier to parse visually? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For future reference: https://en.wikipedia.org/wiki/Klempen
base/iterators.jl
Outdated
flatten_length(f, ::Type{T}) where {T<:Tuple} = isleaftype(T) ? nfields(T)*length(f.it) : throw(ArgumentError("Cannot compute length of a tuple-type which is not a leaf-type")) | ||
|
||
function flatten_length(f, ::Type{<:Tuple}) | ||
if !isleaftype(T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
T
is not defined anymore, adding T::Type{<:Tuple}
in the signature should work
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't function flatten_length(f, ::Type{T}) where T<:Tuple
better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I did not pay attention here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No problem!
@fredrikekre you mean better in style, or... in efficiency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, I thought T::Type
may be less efficient than ::Type{T} where T
, but that it was not the case for T::Type{<:Tuple}
which should be specialized for each different type...
base/iterators.jl
Outdated
end | ||
nfields(T)*length(f.it) | ||
end | ||
flatten_length(f, ::Type{T}) where {T<:Number} = length(f.it) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
flatten_length(f, ::Type{<:Number}) = length(f.it)
? (just a suggestion!)
base/iterators.jl
Outdated
nfields(T)*length(f.it) | ||
end | ||
flatten_length(f, ::Type{T}) where {T<:Number} = length(f.it) | ||
length(f::Flatten{I}) where {I} = flatten_length(f, eltype(I)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For non-(number, tuple), this will throw a MethodError
right? why not a fall-ball flatten_length
which throws a more informative message?
test/iterators.jl
Outdated
@test_throws ArgumentError collect(flatten(Any[])) | ||
@test_throws ArgumentError length(flatten(NTuple[(1,), ()])) # #16680 | ||
@test_throws ArgumentError length(flatten([[1],[1]])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Klempen!
base/iterators.jl
Outdated
end | ||
flatten_length(f, ::Type{<:Number}) = length(f.it) | ||
flatten_length(f, T) = throw(ArgumentError( | ||
"Iterates of the argument to Flatten are not known to have constant length")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Strange indentation, I think this would be better with only 4 spaces
- "Iteratees" wouldn't be the correct word here?
- and as you have a
T
, you might as well report it in the message, e.g."Iteratees (of type $T) ...
Flatten
is private no? could be better to useflatten
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Iteratees would sound funny, even if possibly correct
base/iterators.jl
Outdated
flatten_iteratorsize(a, b) = SizeUnknown() | ||
|
||
function flatten_length(f, ::Type{T}) where {T<:Tuple} | ||
if !isleaftype(T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know if this check is statically resolved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From experience: yes. Can we test that?
base/iterators.jl
Outdated
|
||
function iteratorsize(::Type{Flatten{I}}) where {I} | ||
if isleaftype(eltype(I)) | ||
flatten_iteratorsize(iteratorsize(I), eltype(I)) # eltype is always defined |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With this, you seem to prevent have HasLength
for non-concrete numbers, e.g. Union{Int,UInt}[1, 0x2]
. Why not move this test to flatten_iteratorsize
for tuples, and having simply
iteratorsize(::Type{Flatten{I}}) where {I} = flatten_iteratorsize(iteratorsize(I), eltype(I))
? IOW, similar to what you did for flatten_length
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this would be better.
@@ -347,7 +347,10 @@ end | |||
@test collect(flatten(Any[flatten(Any[1:2, 6:5]), flatten(Any[6:7, 8:9])])) == Any[1,2,6,7,8,9] | |||
@test collect(flatten(Any[2:1])) == Any[] | |||
@test eltype(flatten(UnitRange{Int8}[1:2, 3:4])) == Int8 | |||
@test length(flatten(zip(1:3, 4:6))) == 6 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you could add a test for numbers too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes
base/iterators.jl
Outdated
throw(ArgumentError( | ||
"Cannot compute length of a tuple-type which is not a leaf-type")) | ||
end | ||
nfields(T)*length(f.it) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems that nfields
was deprecated it favor of fieldcount
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this shouldn't have passed tests then. is this covered and running?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As far as I understand, this code path is tested. @mschauer do you know why this was not noticed by CI? (did the change happened after CI was run?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think CI picked up the change, but I did not pick it up (locally on my machine a test regarding output format of arrays is broken recently).
LGTM. Just one question: you are testing tuples with |
Mixed tuples are not NTuples, for this reason I went with the test for leaftype. NTuples which are non-leaf-types could be covered additionally. |
Squashed and rebased. |
iteratorsize(::Type{Flatten{I}}) where {I} = flatten_iteratorsize(iteratorsize(I), eltype(I)) | ||
|
||
function flatten_length(f, ::Type{T}) where {T<:Tuple} | ||
if !isleaftype(T) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it's not very intuitive that isleaftype is the right predicate for this - are Vararg and nesting coupled to this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will merge tomorrow if no-one objects. Finer handling of tuples can be added later if needed. Thanks @mschauer !
hold on at least until there's a good answer for the question above. is this correct even for Tuples with Vararg or nested tuples? |
If a tuple type |
@tkelman good to go now W.R.T. the |
One CI failure is a timeout, the other seems unrelated. |
It is worthwhile to give some
Flatten
-ed iterators the traitHasLength
, if the elements of the iterator which gets flattened are iteratables with type-static length, for exampleTuple
s. This PR does this for iterators with elements of typeTuple
andNumber
.This functionality can then be extended by packages defining iterators with type-encoded length.
For example it allows https://github.com/JuliaArrays/StaticArrays.jl to flatten a
Vector
ofSVector
's efficiently.Replaces #16680