diff --git a/NEWS.md b/NEWS.md index c5bfc9d6ec940..0b7cbb37205ba 100644 --- a/NEWS.md +++ b/NEWS.md @@ -28,11 +28,6 @@ New language features * The postfix conjugate transpose operator `'` now accepts Unicode modifiers as suffixes, so e.g. `a'ᵀ` is parsed as `var"'ᵀ"(a)`, which can be defined by the user. `a'ᵀ` parsed as `a' * ᵀ` before, so this is a minor change ([#37247]). -* It is now possible to use varargs on the left-hand side of assignments for taking any - number of items from the front of an iterable collection, while also collecting the rest, - like `a, b... = [1, 2, 3]`, for example. This syntax is implemented using `Base.rest`, - which can be overloaded to customize its behavior for different collection types - ([#37410]). Language changes ---------------- @@ -101,8 +96,6 @@ New library functions efficiently ([#35816]). * New function `addenv` for adding environment mappings into a `Cmd` object, returning the new `Cmd` object. * New function `insorted` for determining whether an element is in a sorted collection or not ([#37490]). -* New function `Base.rest` for taking the rest of a collection, starting from a specific - iteration state, in a generic way ([#37410]). New library features -------------------- diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 81e351037ae14..df7c22d6a0a04 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -2399,11 +2399,3 @@ function hash(A::AbstractArray, h::UInt) return h end - -# The semantics of `collect` are weird. Better to write our own -function rest(a::AbstractArray{T}, state...) where {T} - v = Vector{T}(undef, 0) - # assume only very few items are taken from the front - sizehint!(v, length(a)) - return foldl(push!, Iterators.rest(a, state...), init=v) -end diff --git a/base/tuple.jl b/base/tuple.jl index ef3a78fc5098d..691b7fb475d8e 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -96,38 +96,6 @@ function indexed_iterate(I, i, state) x end -""" - Base.rest(collection[, itr_state]) - -Generic function for taking the tail of `collection`, starting from a specific iteration -state `itr_state`. Return a `Tuple`, if `collection` itself is a `Tuple`, a `Vector`, if -`collection` is an `AbstractArray` and `Iterators.rest(collection[, itr_state])` otherwise. -Can be overloaded for user-defined collection types to customize the behavior of slurping -in assignments, like `a, b... = collection`. - -!!! compat "Julia 1.6" - `Base.rest` requires at least Julia 1.6. - -# Examples -```jldoctest -julia> a = [1 2; 3 4] -2×2 Matrix{Int64}: - 1 2 - 3 4 - -julia> first, state = iterate(a) -(1, 2) - -julia> first, Base.rest(a, state) -(1, [3, 2, 4]) -``` -""" -function rest end -rest(t::Tuple) = t -rest(t::Tuple, i::Int) = ntuple(x -> getfield(t, x+i-1), length(t)-i+1) -rest(a::Array, i::Int=1) = a[i:end] -rest(itr, state...) = Iterators.rest(itr, state...) - # Use dispatch to avoid a branch in first first(::Tuple{}) = throw(ArgumentError("tuple must be non-empty")) first(t::Tuple) = t[1] diff --git a/doc/src/base/collections.md b/doc/src/base/collections.md index 347d2d33724d4..383dbcda4f93e 100644 --- a/doc/src/base/collections.md +++ b/doc/src/base/collections.md @@ -138,7 +138,6 @@ Base.filter! Base.replace(::Any, ::Pair...) Base.replace(::Base.Callable, ::Any) Base.replace! -Base.rest ``` ## Indexable Collections diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index fc710f90f9b9a..b862d70195246 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1430,8 +1430,7 @@ ,@(reverse after) (unnecessary (tuple ,@(reverse elts)))) (let ((L (car lhss)) - ;; rhss can be null iff L is a vararg - (R (if (null? rhss) '() (car rhss)))) + (R (car rhss))) (cond ((and (symbol-like? L) (or (not (pair? R)) (quoted? R) (equal? R '(null))) ;; overwrite var immediately if it doesn't occur elsewhere @@ -1443,16 +1442,6 @@ (cons (make-assignment L R) stmts) after (cons R elts))) - ((vararg? L) - (if (null? (cdr lhss)) - (let ((temp (make-ssavalue))) - `(block ,@(reverse stmts) - (= ,temp (tuple ,@rhss)) - ,@(reverse after) - (= ,(cadr L) ,temp) - (unnecessary (tuple ,@(reverse elts) (... ,temp))))) - (error (string "invalid \"...\" on non-final assignment location \"" - (cadr L) "\"")))) ((vararg? R) (let ((temp (make-ssavalue))) `(block ,@(reverse stmts) @@ -2077,7 +2066,6 @@ (define (sides-match? l r) ;; l and r either have equal lengths, or r has a trailing ... (cond ((null? l) (null? r)) - ((vararg? (car l)) #t) ((null? r) #f) ((vararg? (car r)) (null? (cdr r))) (else (sides-match? (cdr l) (cdr r))))) @@ -2087,43 +2075,26 @@ (expand-forms (tuple-to-assignments lhss x)) ;; (a, b, ...) = other - (begin - ;; like memq, but if last element of lhss is (... sym), - ;; check against sym instead - (define (in-lhs? x lhss) - (if (null? lhss) - #f - (let ((l (car lhss))) - (cond ((and (pair? l) (eq? (car l) '|...|)) - (if (null? (cdr lhss)) - (eq? (cadr l) x) - (error (string "invalid \"...\" on non-final assignment location \"" - (cadr l) "\"")))) - ((eq? l x) #t) - (else (in-lhs? x (cdr lhss))))))) - ;; in-lhs? also checks for invalid syntax, so always call it first - (let* ((xx (if (or (and (not (in-lhs? x lhss)) (symbol? x)) - (ssavalue? x)) - x (make-ssavalue))) - (ini (if (eq? x xx) '() (list (sink-assignment xx (expand-forms x))))) - (n (length lhss)) - (st (gensy))) - `(block - (local ,st) - ,@ini - ,.(map (lambda (i lhs) - (expand-forms - (if (and (pair? lhs) (eq? (car lhs) '|...|)) - `(= ,(cadr lhs) (call (top rest) ,xx ,.(if (eq? i 0) '() `(,st)))) - (lower-tuple-assignment - (if (= i (- n 1)) - (list lhs) - (list lhs st)) - `(call (top indexed_iterate) - ,xx ,(+ i 1) ,.(if (eq? i 0) '() `(,st))))))) - (iota n) - lhss) - (unnecessary ,xx))))))) + (let* ((xx (if (or (and (symbol? x) (not (memq x lhss))) + (ssavalue? x)) + x (make-ssavalue))) + (ini (if (eq? x xx) '() (list (sink-assignment xx (expand-forms x))))) + (n (length lhss)) + (st (gensy))) + `(block + (local ,st) + ,@ini + ,.(map (lambda (i lhs) + (expand-forms + (lower-tuple-assignment + (if (= i (- n 1)) + (list lhs) + (list lhs st)) + `(call (top indexed_iterate) + ,xx ,(+ i 1) ,.(if (eq? i 0) '() `(,st)))))) + (iota n) + lhss) + (unnecessary ,xx)))))) ((typed_hcat) (error "invalid spacing in left side of indexed assignment")) ((typed_vcat) diff --git a/test/abstractarray.jl b/test/abstractarray.jl index cec1d5092a76f..d09e010bfd2f7 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -1193,10 +1193,3 @@ end @test last(itr, 1) == [itr[end]] @test_throws ArgumentError last(itr, -6) end - -@testset "Base.rest" begin - a = reshape(1:4, 2, 2)' - @test Base.rest(a) == a[:] - _, st = iterate(a) - @test Base.rest(a, st) == [3, 2, 4] -end diff --git a/test/syntax.jl b/test/syntax.jl index 3ad969910a03c..4a14a18a24c75 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2532,54 +2532,3 @@ end # PR #37973 @test Meta.parse("1¦2⌿3") == Expr(:call, :¦, 1, Expr(:call, :⌿, 2, 3)) - -@testset "slurp in assignments" begin - res = begin x, y, z... = 1:7 end - @test res == 1:7 - @test x == 1 && y == 2 - @test z == Vector(3:7) - - res = begin x, y, z... = [1, 2] end - @test res == [1, 2] - @test x == 1 && y == 2 - @test z == Int[] - - x = 1 - res = begin x..., = x end - @test res == 1 - @test x == 1 - - x, y, z... = 1:7 - res = begin y, z, x... = z..., x, y end - @test res == ((3:7)..., 1, 2) - @test y == 3 - @test z == 4 - @test x == ((5:7)..., 1, 2) - - res = begin x, _, y... = 1, 2 end - @test res == (1, 2) - @test x == 1 - @test y == () - - res = begin x, y... = 1 end - @test res == 1 - @test x == 1 - @test y == Iterators.rest(1, nothing) - - res = begin x, y, z... = 1, 2, 3:5 end - @test res == (1, 2, 3:5) - @test x == 1 && y == 2 - @test z == (3:5,) - - @test Meta.isexpr(Meta.@lower(begin a, b..., c = 1:3 end), :error) - @test Meta.isexpr(Meta.@lower(begin a, b..., c = 1, 2, 3 end), :error) - @test Meta.isexpr(Meta.@lower(begin a, b..., c... = 1, 2, 3 end), :error) - - @test_throws BoundsError begin x, y, z... = 1:1 end - @test_throws BoundsError begin x, y, _, z... = 1, 2 end - - car((a, d...)) = a - cdr((a, d...)) = d - @test car(1:3) == 1 - @test cdr(1:3) == [2, 3] -end diff --git a/test/tuple.jl b/test/tuple.jl index 7f5f16e68fff3..21d4932f1ec9b 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -575,21 +575,3 @@ end @test_throws BoundsError (1,2.0)[0:1] @test_throws BoundsError (1,2.0)[0:0] end - -@testset "Base.rest" begin - t = (1, 2.0, 0x03, 4f0) - @test Base.rest(t) === t - @test Base.rest(t, 2) === (2.0, 0x03, 4f0) - - a = [1 2; 3 4] - @test Base.rest(a) == a[:] - @test pointer(Base.rest(a)) != pointer(a) - @test Base.rest(a, 3) == [2, 4] - - itr = (-i for i in a) - @test Base.rest(itr) == itr - _, st = iterate(itr) - r = Base.rest(itr, st) - @test r isa Iterators.Rest - @test collect(r) == -[3, 2, 4] -end