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

Make iterate(::Reverse) use indexing by default #43110

Merged
merged 4 commits into from
Nov 20, 2021

Conversation

MasonProtter
Copy link
Contributor

With this change we can do things like

julia> struct MyVector
           v::Vector
       end

julia> Base.getindex(mv::MyVector, args...) = Base.getindex(mv.v, args...)

julia> Base.eachindex(mv::MyVector) = eachindex(mv.v)

julia> for x  Iterators.reverse(MyVector([:a, :b, :c]))
           @show x
       end
x = :c
x = :b
x = :a

I'm proposing this as an alternative to #43106 in order to fix #43101

@MasonProtter
Copy link
Contributor Author

I think the most significant downside I can forsee of this is that if one has a type that does not support indexing, then you may get worse error messages than before.

Compare (old)

julia> struct Foo
           a::Int
           b::Int
       end

julia> for x  Iterators.reverse(Foo(1, 2))
           @show x
       end
ERROR: MethodError: no method matching iterate(::Base.Iterators.Reverse{Foo})
Closest candidates are:
  iterate(::Union{LinRange, StepRangeLen}) at ~/julia/usr/share/julia/base/range.jl:821
  iterate(::Union{LinRange, StepRangeLen}, ::Integer) at ~/julia/usr/share/julia/base/range.jl:821
  iterate(::T) where T<:Union{Base.KeySet{<:Any, <:Dict}, Base.ValueIterator{<:Dict}} at ~/julia/usr/share/julia/base/dict.jl:695
  ...
Stacktrace:
 [1] top-level scope
   @ ./REPL[2]:1

against (new)

julia> struct Foo
           a::Int
           b::Int
       end

julia> for x  Iterators.reverse(Foo(1, 2))
           @show x
       end
ERROR: MethodError: no method matching keys(::Foo)
Closest candidates are:
  keys(::IndexStyle, ::AbstractArray, ::AbstractArray...) at ~/julia/usr/share/julia/base/abstractarray.jl:350
  keys(::Tuple) at ~/julia/usr/share/julia/base/tuple.jl:72
  keys(::Tuple, ::Tuple...) at ~/julia/usr/share/julia/base/tuple.jl:77
  ...
Stacktrace:
 [1] eachindex(itrs::Foo)
   @ Base ./abstractarray.jl:276
 [2] iterate(A::Base.Iterators.Reverse{Foo})
   @ Base.Iterators ./REPL[1]:2
 [3] top-level scope
   @ ./REPL[7]:1

@vtjnash
Copy link
Member

vtjnash commented Nov 17, 2021

I think we should do this in addition to #43106

@Seelengrab
Copy link
Contributor

Since Iterators.reverse is a lazy wrapper anyway, we could check isapplicable on construction and manually throw a MethodError, right? Wouldn't be pretty, but the construction itself is probably not performance critical and it wouldn't be the first time a custom MethodError is thrown for clarity sake.

@JeffBezanson JeffBezanson added the triage This should be discussed on a triage call label Nov 17, 2021
@@ -112,6 +112,15 @@ last(r::Reverse) = first(r.itr) # the first shall be last
(A.itr[idx], (state[1], itrs))
end

# Fallback method of `iterate(::Reverse{T})` which assumes the collection has `getindex(::T) and `reverse(eachindex(::T))`
# don't propagate inbounds for this just in case
function iterate(A::Reverse state=(reverse(eachindex(A.itr)),))
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
function iterate(A::Reverse state=(reverse(eachindex(A.itr)),))
function iterate(A::Reverse, state=(reverse(eachindex(A.itr)),))

I guess this could lead to a dispatch loop if an array's index iterator doesn't support reverse? Maybe ok though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe use Base.reverse?

Co-authored-by: Jeff Bezanson <[email protected]>
@MasonProtter
Copy link
Contributor Author

I think we should do this in addition to #43106

But #43106 makes last use indexing instead of reverse by default..

@JeffBezanson
Copy link
Member

Our thinking is that implementing last is easier (and could be implemented for strictly more types) than implementing reverse iteration, so we might as well have both fall back to indexing, and types with reverse iteration should just add last methods.

@vtjnash vtjnash removed the triage This should be discussed on a triage call label Nov 19, 2021
@vtjnash vtjnash added the merge me PR is reviewed. Merge when all tests are passing label Nov 19, 2021
@DilumAluthge DilumAluthge merged commit b64f1cd into JuliaLang:master Nov 20, 2021
@DilumAluthge DilumAluthge removed the merge me PR is reviewed. Merge when all tests are passing label Nov 20, 2021
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Feb 22, 2022
Co-authored-by: Jeff Bezanson <[email protected]>

Co-authored-by: Jeff Bezanson <[email protected]>
Co-authored-by: Jameson Nash <[email protected]>
LilithHafner pushed a commit to LilithHafner/julia that referenced this pull request Mar 8, 2022
Co-authored-by: Jeff Bezanson <[email protected]>

Co-authored-by: Jeff Bezanson <[email protected]>
Co-authored-by: Jameson Nash <[email protected]>
@MasonProtter MasonProtter deleted the patch-4 branch October 30, 2024 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

last now broken for custom iterators on master
5 participants