Skip to content

Commit 8c0c2c9

Browse files
committed
Add MapView to stdlib
1 parent 0eb57a7 commit 8c0c2c9

File tree

2 files changed

+203
-5
lines changed

2 files changed

+203
-5
lines changed

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

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import scala.collection.generic.DefaultSerializable
1818
import scala.collection.mutable.StringBuilder
1919
import scala.util.hashing.MurmurHash3
2020
import language.experimental.captureChecking
21+
import caps.unsafe.unsafeAssumePure
2122

2223
/** Base Map type */
2324
trait Map[K, +V]
@@ -103,8 +104,9 @@ trait Map[K, +V]
103104
trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
104105
extends IterableOps[(K, V), Iterable, C]
105106
with PartialFunction[K, V] {
107+
this: MapOps[K, V, CC, C]^ =>
106108

107-
override def view: MapView[K, V] = new MapView.Id(this)
109+
override def view: MapView[K, V]^{this} = new MapView.Id(this)
108110

109111
/** Returns a [[Stepper]] for the keys of this map. See method [[stepper]]. */
110112
def keyStepper[S <: Stepper[_]](implicit shape: StepperShape[K, S]): S = {
@@ -253,15 +255,15 @@ trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
253255
* the predicate `p`. The resulting map wraps the original map without copying any elements.
254256
*/
255257
@deprecated("Use .view.filterKeys(f). A future version will include a strict version of this method (for now, .view.filterKeys(p).toMap).", "2.13.0")
256-
def filterKeys(p: K => Boolean): MapView[K, V] = new MapView.FilterKeys(this, p)
258+
def filterKeys(p: K => Boolean): MapView[K, V]^{this, p} = new MapView.FilterKeys(this, p)
257259

258260
/** Transforms this map by applying a function to every retrieved value.
259261
* @param f the function used to transform values of this map.
260262
* @return a map view which maps every key of this map
261263
* to `f(this(key))`. The resulting map wraps the original map without copying any elements.
262264
*/
263265
@deprecated("Use .view.mapValues(f). A future version will include a strict version of this method (for now, .view.mapValues(f).toMap).", "2.13.0")
264-
def mapValues[W](f: V => W): MapView[K, W] = new MapView.MapValues(this, f)
266+
def mapValues[W](f: V => W): MapView[K, W]^{this, f} = new MapView.MapValues(this, f)
265267

266268
/** Defines the default value computation for the map,
267269
* returned when a key is not found
@@ -354,7 +356,7 @@ trait MapOps[K, +V, +CC[_, _] <: IterableOps[_, AnyConstr, _], +C]
354356
@deprecated("Consider requiring an immutable Map.", "2.13.0")
355357
@`inline` def -- (keys: IterableOnce[K]^): C = {
356358
lazy val keysSet = keys.iterator.to(immutable.Set)
357-
fromSpecific(this.view.filterKeys(k => !keysSet.contains(k)))
359+
fromSpecific(this.view.filterKeys(k => !keysSet.contains(k))).unsafeAssumePure
358360
}
359361

360362
@deprecated("Use ++ instead of ++: for collections of type Iterable", "2.13.0")
@@ -375,7 +377,7 @@ object MapOps {
375377
*/
376378
@SerialVersionUID(3L)
377379
class WithFilter[K, +V, +IterableCC[_], +CC[_, _] <: IterableOps[_, AnyConstr, _]](
378-
self: MapOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _],
380+
self: (MapOps[K, V, CC, _] with IterableOps[(K, V), IterableCC, _])^,
379381
p: ((K, V)) => Boolean
380382
) extends IterableOps.WithFilter[(K, V), IterableCC](self, p) with Serializable {
381383

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
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.collection
14+
15+
import scala.annotation.nowarn
16+
import scala.collection.MapView.SomeMapOps
17+
import scala.collection.mutable.Builder
18+
import language.experimental.captureChecking
19+
import caps.unsafe.unsafeAssumePure
20+
21+
trait MapView[K, +V]
22+
extends MapOps[K, V, ({ type l[X, Y] = View[(X, Y)] })#l, View[(K, V)]]
23+
with View[(K, V)] {
24+
this: MapView[K, V]^ =>
25+
26+
override def view: MapView[K, V]^{this} = this
27+
28+
// Ideally this returns a `View`, but bincompat
29+
/** Creates a view over all keys of this map.
30+
*
31+
* @return the keys of this map as a view.
32+
*/
33+
override def keys: Iterable[K]^{this} = new MapView.Keys(this)
34+
35+
// Ideally this returns a `View`, but bincompat
36+
/** Creates a view over all values of this map.
37+
*
38+
* @return the values of this map as a view.
39+
*/
40+
override def values: Iterable[V]^{this} = new MapView.Values(this)
41+
42+
/** Filters this map by retaining only keys satisfying a predicate.
43+
* @param p the predicate used to test keys
44+
* @return an immutable map consisting only of those key value pairs of this map where the key satisfies
45+
* the predicate `p`. The resulting map wraps the original map without copying any elements.
46+
*/
47+
override def filterKeys(p: K => Boolean): MapView[K, V]^{this, p} = new MapView.FilterKeys(this, p)
48+
49+
/** Transforms this map by applying a function to every retrieved value.
50+
* @param f the function used to transform values of this map.
51+
* @return a map view which maps every key of this map
52+
* to `f(this(key))`. The resulting map wraps the original map without copying any elements.
53+
*/
54+
override def mapValues[W](f: V => W): MapView[K, W]^{this, f} = new MapView.MapValues(this, f)
55+
56+
override def filter(pred: ((K, V)) => Boolean): MapView[K, V]^{this, pred} = new MapView.Filter(this, false, pred)
57+
58+
override def filterNot(pred: ((K, V)) => Boolean): MapView[K, V]^{this, pred} = new MapView.Filter(this, true, pred)
59+
60+
override def partition(p: ((K, V)) => Boolean): (MapView[K, V]^{this, p}, MapView[K, V]^{this, p}) = (filter(p), filterNot(p))
61+
62+
override def tapEach[U](f: ((K, V)) => U): MapView[K, V]^{this, f} = new MapView.TapEach(this, f)
63+
64+
def mapFactory: MapViewFactory = MapView
65+
66+
override def empty: MapView[K, V] = mapFactory.empty
67+
68+
override def withFilter(p: ((K, V)) => Boolean): MapOps.WithFilter[K, V, View, ({ type l[X, Y] = View[(X, Y)] })#l]^{this, p} = new MapOps.WithFilter(this, p)
69+
70+
override def toString: String = super[View].toString
71+
72+
@nowarn("""cat=deprecation&origin=scala\.collection\.Iterable\.stringPrefix""")
73+
override protected[this] def stringPrefix: String = "MapView"
74+
}
75+
76+
object MapView extends MapViewFactory {
77+
78+
/** An `IterableOps` whose collection type and collection type constructor are unknown */
79+
type SomeIterableConstr[X, Y] = IterableOps[_, AnyConstr, _]
80+
/** A `MapOps` whose collection type and collection type constructor are (mostly) unknown */
81+
type SomeMapOps[K, +V] = MapOps[K, V, SomeIterableConstr, _]
82+
83+
@SerialVersionUID(3L)
84+
object EmptyMapView extends AbstractMapView[Any, Nothing] {
85+
// !!! cc problem: crash when we replace the line with
86+
// private val EmptyMapView: MapView[Any, Nothing] = new AbstractMapView[Any, Nothing] {
87+
override def get(key: Any): Option[Nothing] = None
88+
override def iterator: Iterator[Nothing] = Iterator.empty[Nothing]
89+
override def knownSize: Int = 0
90+
override def isEmpty: Boolean = true
91+
override def filterKeys(p: Any => Boolean): MapView[Any, Nothing] = this
92+
override def mapValues[W](f: Nothing => W): MapView[Any, Nothing] = this
93+
override def filter(pred: ((Any, Nothing)) => Boolean): MapView[Any, Nothing] = this
94+
override def filterNot(pred: ((Any, Nothing)) => Boolean): MapView[Any, Nothing] = this
95+
override def partition(p: ((Any, Nothing)) => Boolean): (MapView[Any, Nothing], MapView[Any, Nothing]) = (this, this)
96+
}
97+
98+
@SerialVersionUID(3L)
99+
class Id[K, +V](underlying: SomeMapOps[K, V]^) extends AbstractMapView[K, V] {
100+
def get(key: K): Option[V] = underlying.get(key)
101+
def iterator: Iterator[(K, V)]^{this} = underlying.iterator
102+
override def knownSize: Int = underlying.knownSize
103+
override def isEmpty: Boolean = underlying.isEmpty
104+
}
105+
106+
// Ideally this is public, but bincompat
107+
@SerialVersionUID(3L)
108+
private class Keys[K](underlying: SomeMapOps[K, _]^) extends AbstractView[K] {
109+
def iterator: Iterator[K]^{this} = underlying.keysIterator
110+
override def knownSize: Int = underlying.knownSize
111+
override def isEmpty: Boolean = underlying.isEmpty
112+
}
113+
114+
// Ideally this is public, but bincompat
115+
@SerialVersionUID(3L)
116+
private class Values[+V](underlying: SomeMapOps[_, V]^) extends AbstractView[V] {
117+
def iterator: Iterator[V]^{this} = underlying.valuesIterator
118+
override def knownSize: Int = underlying.knownSize
119+
override def isEmpty: Boolean = underlying.isEmpty
120+
}
121+
122+
@SerialVersionUID(3L)
123+
class MapValues[K, +V, +W](underlying: SomeMapOps[K, V]^, f: V => W) extends AbstractMapView[K, W] {
124+
def iterator: Iterator[(K, W)]^{this} = underlying.iterator.map(kv => (kv._1, f(kv._2)))
125+
def get(key: K): Option[W] = underlying.get(key).map(f)
126+
override def knownSize: Int = underlying.knownSize
127+
override def isEmpty: Boolean = underlying.isEmpty
128+
}
129+
130+
@SerialVersionUID(3L)
131+
class FilterKeys[K, +V](underlying: SomeMapOps[K, V]^, p: K => Boolean) extends AbstractMapView[K, V] {
132+
def iterator: Iterator[(K, V)]^{this} = underlying.iterator.filter { case (k, _) => p(k) }
133+
def get(key: K): Option[V] = if (p(key)) underlying.get(key) else None
134+
override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize
135+
override def isEmpty: Boolean = iterator.isEmpty
136+
}
137+
138+
@SerialVersionUID(3L)
139+
class Filter[K, +V](underlying: SomeMapOps[K, V]^, isFlipped: Boolean, p: ((K, V)) => Boolean) extends AbstractMapView[K, V] {
140+
def iterator: Iterator[(K, V)]^{this} = underlying.iterator.filterImpl(p, isFlipped)
141+
def get(key: K): Option[V] = underlying.get(key) match {
142+
case s @ Some(v) if p((key, v)) != isFlipped => s
143+
case _ => None
144+
}
145+
override def knownSize: Int = if (underlying.knownSize == 0) 0 else super.knownSize
146+
override def isEmpty: Boolean = iterator.isEmpty
147+
}
148+
149+
@SerialVersionUID(3L)
150+
class TapEach[K, +V, +U](underlying: SomeMapOps[K, V]^, f: ((K, V)) => U) extends AbstractMapView[K, V] {
151+
override def get(key: K): Option[V] = {
152+
underlying.get(key) match {
153+
case s @ Some(v) =>
154+
f((key, v))
155+
s
156+
case None => None
157+
}
158+
}
159+
override def iterator: Iterator[(K, V)]^{this} = underlying.iterator.tapEach(f)
160+
override def knownSize: Int = underlying.knownSize
161+
override def isEmpty: Boolean = underlying.isEmpty
162+
}
163+
164+
override def newBuilder[sealed X, sealed Y]: Builder[(X, Y), MapView[X, Y]] = mutable.HashMap.newBuilder[X, Y].mapResult(_.view)
165+
166+
override def empty[K, V]: MapView[K, V] = EmptyMapView.asInstanceOf[MapView[K, V]]
167+
168+
override def from[K, V](it: IterableOnce[(K, V)]^): View[(K, V)] =
169+
View.from(it).unsafeAssumePure
170+
// unsafeAssumePure needed here since MapViewFactory inherits from MapFactory,
171+
// and the latter assumes maps are strict, so from's result captures nothing.
172+
173+
override def from[K, V](it: SomeMapOps[K, V]^): MapView[K, V]^{it} = it match {
174+
case mv: MapView[K, V] => mv
175+
case other => new MapView.Id(other)
176+
}
177+
178+
override def apply[K, V](elems: (K, V)*): MapView[K, V] = from(elems.toMap)
179+
}
180+
181+
trait MapViewFactory extends collection.MapFactory[({ type l[X, Y] = View[(X, Y)]})#l] {
182+
183+
def newBuilder[X, Y]: Builder[(X, Y), MapView[X, Y]]
184+
185+
def empty[X, Y]: MapView[X, Y]
186+
187+
def from[K, V](it: SomeMapOps[K, V]^): MapView[K, V]^{it}
188+
189+
override def apply[K, V](elems: (K, V)*): MapView[K, V] = from(elems.toMap)
190+
}
191+
192+
/** Explicit instantiation of the `MapView` trait to reduce class file size in subclasses. */
193+
@SerialVersionUID(3L)
194+
abstract class AbstractMapView[K, +V] extends AbstractView[(K, V)] with MapView[K, V]:
195+
this: AbstractMapView[K, V]^ =>
196+

0 commit comments

Comments
 (0)