Skip to content

Commit 35f2246

Browse files
committed
Add SeqView to stdlib
1 parent 8f76d63 commit 35f2246

File tree

2 files changed

+233
-2
lines changed

2 files changed

+233
-2
lines changed

tests/pos-special/stdlib/collection/Seq.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ object Seq extends SeqFactory.Delegate[Seq](immutable.Seq)
7777
* @define coll sequence
7878
* @define Coll `Seq`
7979
*/
80-
trait SeqOps[+A, +CC[_], +C] extends Any with IterableOps[A, CC, C] { self =>
80+
trait SeqOps[+A, +CC[_], +C] extends Any with SeqViewOps[A, CC, C] { self =>
8181

8282
override def view: SeqView[A] = new SeqView.Id[A](this)
8383

@@ -234,7 +234,7 @@ trait SeqOps[+A, +CC[_], +C] extends Any with IterableOps[A, CC, C] { self =>
234234
*
235235
* @return an iterator yielding the elements of this $coll in reversed order
236236
*/
237-
def reverseIterator: Iterator[A] = reversed.iterator
237+
override def reverseIterator: Iterator[A] = reversed.iterator
238238

239239
/** Tests whether this $coll contains the given sequence at a given index.
240240
*
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala
14+
package collection
15+
16+
import scala.annotation.nowarn
17+
import language.experimental.captureChecking
18+
import caps.unsafe.unsafeAssumePure
19+
20+
/** !!! Scala 2 difference: Need intermediate trait SeqViewOps to collect the
21+
* necessary functionality over which SeqViews are defined, and at the same
22+
* time allowing impure operations. Scala 2 uses SeqOps here, but SeqOps is
23+
* pure, whereas SeqViews are Iterables which can be impure (for instance,
24+
* mapping a SeqView with an impure function gives an impure view).
25+
*/
26+
trait SeqViewOps[+A, +CC[_], +C] extends Any with IterableOps[A, CC, C] {
27+
self: SeqViewOps[A, CC, C]^ =>
28+
29+
def length: Int
30+
def apply(x: Int): A
31+
def appended[B >: A](elem: B): CC[B]^{this}
32+
def prepended[B >: A](elem: B): CC[B]^{this}
33+
def reverse: C^{this}
34+
def sorted[B >: A](implicit ord: Ordering[B]): C^{this}
35+
36+
def reverseIterator: Iterator[A]^{this} = reversed.iterator
37+
}
38+
39+
trait SeqView[+A] extends SeqViewOps[A, View, View[A]] with View[A] {
40+
self: SeqView[A]^ =>
41+
42+
override def view: SeqView[A]^{this} = this
43+
44+
override def map[B](f: A => B): SeqView[B]^{this, f} = new SeqView.Map(this, f)
45+
override def appended[B >: A](elem: B): SeqView[B]^{this} = new SeqView.Appended(this, elem)
46+
override def prepended[B >: A](elem: B): SeqView[B]^{this} = new SeqView.Prepended(elem, this)
47+
override def reverse: SeqView[A]^{this} = new SeqView.Reverse(this)
48+
override def take(n: Int): SeqView[A]^{this} = new SeqView.Take(this, n)
49+
override def drop(n: Int): SeqView[A]^{this} = new SeqView.Drop(this, n)
50+
override def takeRight(n: Int): SeqView[A]^{this} = new SeqView.TakeRight(this, n)
51+
override def dropRight(n: Int): SeqView[A]^{this} = new SeqView.DropRight(this, n)
52+
override def tapEach[U](f: A => U): SeqView[A]^{this, f} = new SeqView.Map(this, { (a: A) => f(a); a })
53+
54+
def concat[B >: A](suffix: SeqView.SomeSeqOps[B]): SeqView[B]^{this} = new SeqView.Concat(this, suffix)
55+
def appendedAll[B >: A](suffix: SeqView.SomeSeqOps[B]): SeqView[B]^{this} = new SeqView.Concat(this, suffix)
56+
def prependedAll[B >: A](prefix: SeqView.SomeSeqOps[B]): SeqView[B]^{this} = new SeqView.Concat(prefix, this)
57+
58+
override def sorted[B >: A](implicit ord: Ordering[B]): SeqView[A]^{this} = new SeqView.Sorted(this, ord)
59+
60+
@nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""")
61+
override protected[this] def stringPrefix: String = "SeqView"
62+
}
63+
64+
object SeqView {
65+
66+
/** A `SeqOps` whose collection type and collection type constructor are unknown */
67+
private type SomeSeqOps[+A] = SeqViewOps[A, AnyConstr, _]
68+
69+
/** A view that doesn’t apply any transformation to an underlying sequence */
70+
@SerialVersionUID(3L)
71+
class Id[+A](underlying: SomeSeqOps[A]^) extends AbstractSeqView[A] {
72+
def apply(idx: Int): A = underlying.apply(idx)
73+
def length: Int = underlying.length
74+
def iterator: Iterator[A]^{this} = underlying.iterator
75+
override def knownSize: Int = underlying.knownSize
76+
override def isEmpty: Boolean = underlying.isEmpty
77+
}
78+
79+
@SerialVersionUID(3L)
80+
class Map[+A, +B](underlying: SomeSeqOps[A]^, f: A => B) extends View.Map[A, B](underlying, f) with SeqView[B] {
81+
def apply(idx: Int): B = f(underlying(idx))
82+
def length: Int = underlying.length
83+
}
84+
85+
@SerialVersionUID(3L)
86+
class Appended[+A](underlying: SomeSeqOps[A]^, elem: A) extends View.Appended(underlying, elem) with SeqView[A] {
87+
def apply(idx: Int): A = if (idx == underlying.length) elem else underlying(idx)
88+
def length: Int = underlying.length + 1
89+
}
90+
91+
@SerialVersionUID(3L)
92+
class Prepended[+A](elem: A, underlying: SomeSeqOps[A]^) extends View.Prepended(elem, underlying) with SeqView[A] {
93+
def apply(idx: Int): A = if (idx == 0) elem else underlying(idx - 1)
94+
def length: Int = underlying.length + 1
95+
}
96+
97+
@SerialVersionUID(3L)
98+
class Concat[A](prefix: SomeSeqOps[A]^, suffix: SomeSeqOps[A]^) extends View.Concat[A](prefix, suffix) with SeqView[A] {
99+
def apply(idx: Int): A = {
100+
val l = prefix.length
101+
if (idx < l) prefix(idx) else suffix(idx - l)
102+
}
103+
def length: Int = prefix.length + suffix.length
104+
}
105+
106+
@SerialVersionUID(3L)
107+
class Reverse[A](underlying: SomeSeqOps[A]^) extends AbstractSeqView[A] {
108+
def apply(i: Int) = underlying.apply(size - 1 - i)
109+
def length = underlying.size
110+
def iterator: Iterator[A]^{this} = underlying.reverseIterator
111+
override def knownSize: Int = underlying.knownSize
112+
override def isEmpty: Boolean = underlying.isEmpty
113+
}
114+
115+
@SerialVersionUID(3L)
116+
class Take[+A](underlying: SomeSeqOps[A]^, n: Int) extends View.Take(underlying, n) with SeqView[A] {
117+
def apply(idx: Int): A = if (idx < n) {
118+
underlying(idx)
119+
} else {
120+
throw new IndexOutOfBoundsException(s"$idx is out of bounds (min 0, max ${if (underlying.knownSize >= 0) knownSize - 1 else "unknown"})")
121+
}
122+
def length: Int = underlying.length min normN
123+
}
124+
125+
@SerialVersionUID(3L)
126+
class TakeRight[+A](underlying: SomeSeqOps[A]^, n: Int) extends View.TakeRight(underlying, n) with SeqView[A] {
127+
private[this] val delta = (underlying.size - (n max 0)) max 0
128+
def length = underlying.size - delta
129+
@throws[IndexOutOfBoundsException]
130+
def apply(i: Int) = underlying.apply(i + delta)
131+
}
132+
133+
@SerialVersionUID(3L)
134+
class Drop[A](underlying: SomeSeqOps[A]^, n: Int) extends View.Drop[A](underlying, n) with SeqView[A] {
135+
def length = (underlying.size - normN) max 0
136+
@throws[IndexOutOfBoundsException]
137+
def apply(i: Int) = underlying.apply(i + normN)
138+
override def drop(n: Int): SeqView[A]^{this} = new Drop(underlying, this.n + n)
139+
}
140+
141+
@SerialVersionUID(3L)
142+
class DropRight[A](underlying: SomeSeqOps[A]^, n: Int) extends View.DropRight[A](underlying, n) with SeqView[A] {
143+
private[this] val len = (underlying.size - (n max 0)) max 0
144+
def length = len
145+
@throws[IndexOutOfBoundsException]
146+
def apply(i: Int) = underlying.apply(i)
147+
}
148+
149+
@SerialVersionUID(3L)
150+
class Sorted[A, B >: A] private (private[this] var underlying: SomeSeqOps[A]^,
151+
private[this] val len: Int,
152+
ord: Ordering[B])
153+
extends SeqView[A] {
154+
outer: Sorted[A, B]^ =>
155+
156+
// force evaluation immediately by calling `length` so infinite collections
157+
// hang on `sorted`/`sortWith`/`sortBy` rather than on arbitrary method calls
158+
def this(underlying: SomeSeqOps[A]^, ord: Ordering[B]) = this(underlying, underlying.length, ord)
159+
160+
@SerialVersionUID(3L)
161+
private[this] class ReverseSorted extends SeqView[A] {
162+
private[this] lazy val _reversed = new SeqView.Reverse(_sorted)
163+
164+
def apply(i: Int): A = _reversed.apply(i)
165+
def length: Int = len
166+
def iterator: Iterator[A]^{this} = Iterator.empty ++ _reversed.iterator // very lazy
167+
override def knownSize: Int = len
168+
override def isEmpty: Boolean = len == 0
169+
override def to[C1](factory: Factory[A, C1]): C1 = _reversed.to(factory)
170+
override def reverse: SeqView[A]^{outer} = outer
171+
override protected def reversed: Iterable[A] = outer.unsafeAssumePure
172+
173+
override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A]^{this} =
174+
if (ord1 == Sorted.this.ord) outer.unsafeAssumePure
175+
else if (ord1.isReverseOf(Sorted.this.ord)) this
176+
else new Sorted(elems, len, ord1)
177+
}
178+
179+
@volatile private[this] var evaluated = false
180+
181+
private[this] lazy val _sorted: Seq[A] = {
182+
val res = {
183+
val len = this.len
184+
if (len == 0) Nil
185+
else if (len == 1) List(underlying.head)
186+
else {
187+
val arr = new Array[Any](len) // Array[Any] =:= Array[AnyRef]
188+
underlying.copyToArray(arr)
189+
java.util.Arrays.sort(arr.asInstanceOf[Array[AnyRef]], ord.asInstanceOf[Ordering[AnyRef]])
190+
// casting the Array[AnyRef] to Array[A] and creating an ArraySeq from it
191+
// is safe because:
192+
// - the ArraySeq is immutable, and items that are not of type A
193+
// cannot be added to it
194+
// - we know it only contains items of type A (and if this collection
195+
// contains items of another type, we'd get a CCE anyway)
196+
// - the cast doesn't actually do anything in the runtime because the
197+
// type of A is not known and Array[_] is Array[AnyRef]
198+
immutable.ArraySeq.unsafeWrapArray(arr.asInstanceOf[Array[A]])
199+
}
200+
}
201+
evaluated = true
202+
underlying = null
203+
res
204+
}
205+
206+
private[this] def elems: SomeSeqOps[A]^{this} = {
207+
val orig = underlying
208+
if (evaluated) _sorted else orig
209+
}
210+
211+
def apply(i: Int): A = _sorted.apply(i)
212+
def length: Int = len
213+
def iterator: Iterator[A]^{this} = Iterator.empty ++ _sorted.iterator // very lazy
214+
override def knownSize: Int = len
215+
override def isEmpty: Boolean = len == 0
216+
override def to[C1](factory: Factory[A, C1]): C1 = _sorted.to(factory)
217+
override def reverse: SeqView[A] = new ReverseSorted
218+
// we know `_sorted` is either tiny or has efficient random access,
219+
// so this is acceptable for `reversed`
220+
override protected def reversed: Iterable[A] = new ReverseSorted
221+
222+
override def sorted[B1 >: A](implicit ord1: Ordering[B1]): SeqView[A]^{this} =
223+
if (ord1 == this.ord) this
224+
else if (ord1.isReverseOf(this.ord)) reverse
225+
else new Sorted(elems, len, ord1)
226+
}
227+
}
228+
229+
/** Explicit instantiation of the `SeqView` trait to reduce class file size in subclasses. */
230+
@SerialVersionUID(3L)
231+
abstract class AbstractSeqView[+A] extends AbstractView[A] with SeqView[A]

0 commit comments

Comments
 (0)