Skip to content
This repository has been archived by the owner on Dec 22, 2021. It is now read-only.

Add withFilter #165

Merged
merged 5 commits into from
Jul 27, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/main/scala/strawman/collection/Iterable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,23 @@ trait IterableOps[+A, +CC[X], +C] extends Any {
*/
def filterNot(pred: A => Boolean): C = fromSpecificIterable(View.Filter(coll, (a: A) => !pred(a)))

/** Creates a non-strict filter of this $coll.
*
* Note: the difference between `c filter p` and `c withFilter p` is that
* the former creates a new collection, whereas the latter only
* restricts the domain of subsequent `map`, `flatMap`, `foreach`,
* and `withFilter` operations.
* $orderDependent
*
* @param p the predicate used to test elements.
* @return an object of class `WithFilter`, which supports
* `map`, `flatMap`, `foreach`, and `withFilter` operations.
* All these operations apply to those elements of this $coll
* which satisfy the predicate `p`.
*/
def withFilter(p: A => Boolean): WithFilter[A, CC] =
new WithFilter(View.Filter(coll, p), iterableFactory)

/** A pair of, first, all elements that satisfy prediacte `p` and, second,
* all elements that do not. Interesting because it splits a collection in two.
*
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/strawman/collection/Map.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ trait MapOps[K, +V, +CC[X, Y] <: Map[X, Y], +C <: Map[K, V]]
*/
def empty: C

override def withFilter(p: ((K, V)) => Boolean): MapWithFilter[K, V, Iterable, CC] =
new MapWithFilter(View.Filter(coll, p), iterableFactory, mapFactory)

def map[K2, V2](f: ((K, V)) => (K2, V2)): CC[K2, V2] = mapFromIterable(View.Map(coll, f))

def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC[K2, V2] = mapFromIterable(View.FlatMap(coll, f))
Expand Down
5 changes: 4 additions & 1 deletion src/main/scala/strawman/collection/SortedMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package collection

import strawman.collection.immutable.TreeMap

import scala.{`inline`, Ordering, PartialFunction}
import scala.{Boolean, `inline`, Ordering, PartialFunction}

/** Base type of sorted sets */
trait SortedMap[K, +V]
Expand All @@ -23,6 +23,9 @@ trait SortedMapOps[K, +V, +CC[X, Y] <: SortedMap[X, Y] with SortedMapOps[X, Y, C
def firstKey: K = head._1
def lastKey: K = last._1

override def withFilter(p: ((K, V)) => Boolean): SortedMapWithFilter[K, V, Iterable, Map, CC] =
new SortedMapWithFilter(View.Filter(coll, p), iterableFactory, mapFactory, sortedMapFactory)

// And finally, we add new overloads taking an ordering
def map[K2, V2](f: ((K, V)) => (K2, V2))(implicit ordering: Ordering[K2]): CC[K2, V2] =
sortedMapFromIterable(View.Map[(K, V), (K2, V2)](coll, f))
Expand Down
7 changes: 5 additions & 2 deletions src/main/scala/strawman/collection/SortedSet.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package strawman.collection

import scala.{Ordering, `inline`}
import scala.{Boolean, Ordering, `inline`}
import scala.annotation.unchecked.uncheckedVariance

/** Base type of sorted sets */
trait SortedSet[A] extends Set[A] with SortedSetOps[A, SortedSet, SortedSet[A]]

trait SortedSetOps[A, +CC[X], +C <: SortedSet[A]]
trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSet[A]]
extends SetOps[A, Set, C]
with SortedOps[A, CC, C] {

Expand All @@ -15,6 +15,9 @@ trait SortedSetOps[A, +CC[X], +C <: SortedSet[A]]
def firstKey: A = head
def lastKey: A = last

override def withFilter(p: A => Boolean): SortedWithFilter[A, Set, CC] =
new SortedWithFilter(View.Filter(coll, p), iterableFactory, sortedIterableFactory)

/** Map */
def map[B : Ordering](f: A => B): CC[B] = sortedFromIterable(View.Map(coll, f))

Expand Down
10 changes: 9 additions & 1 deletion src/main/scala/strawman/collection/View.scala
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,18 @@ object View extends IterableFactory[View] {
}

/** A view that filters an underlying collection. */
case class Filter[A](underlying: Iterable[A], p: A => Boolean) extends View[A] {
class Filter[A](val underlying: Iterable[A], val p: A => Boolean) extends View[A] {
def iterator() = underlying.iterator().filter(p)
}

object Filter {
def apply[A](underlying: Iterable[A], p: A => Boolean): Filter[A] =
underlying match {
case filter: Filter[A] => new Filter(filter.underlying, a => filter.p(a) && p(a))
case _ => new Filter(underlying, p)
}
}

/** A view that partitions an underlying collection into two views */
case class Partition[A](underlying: Iterable[A], p: A => Boolean) {

Expand Down
70 changes: 70 additions & 0 deletions src/main/scala/strawman/collection/WithFilter.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package strawman
package collection

import scala.{Boolean, Ordering, Unit}

/** A template trait that contains just the `map`, `flatMap`, `foreach` and `withFilter` methods
* of trait `Iterable`.
*
* @tparam A Element type (e.g. `Int`)
* @tparam CC Collection type constructor (e.g. `List`)
*/
class WithFilter[+A, +CC[_]](filtered: View[A], factory: IterableFactory[CC]) {

def map[B](f: A => B): CC[B] = factory.fromIterable(View.Map(filtered, f))

def flatMap[B](f: A => IterableOnce[B]): CC[B] = factory.fromIterable(View.FlatMap(filtered, f))

def foreach[U](f: A => U): Unit = filtered.foreach(f)

def withFilter(p: A => Boolean): WithFilter[A, CC] = new WithFilter(filtered.filter(p), factory)

}

class SortedWithFilter[+A, +CC1[_], +CC2[X] <: Iterable[X]](
filtered: View[A],
iterableFactory: IterableFactory[CC1],
sortedFactory: SortedIterableFactory[CC2]
) extends WithFilter[A, CC1](filtered, iterableFactory) {

def map[B : Ordering](f: A => B): CC2[B] = sortedFactory.sortedFromIterable(View.Map(filtered, f))

def flatMap[B : Ordering](f: A => IterableOnce[B]): CC2[B] = sortedFactory.sortedFromIterable(View.FlatMap(filtered, f))

override def withFilter(p: A => Boolean): SortedWithFilter[A, CC1, CC2] =
new SortedWithFilter(filtered.filter(p), iterableFactory, sortedFactory)

}

class MapWithFilter[K, +V, +CC1[_], +CC2[X, Y] <: Map[X, Y]](
filtered: View[(K, V)],
iterableFactory: IterableFactory[CC1],
mapFactory: MapFactory[CC2]
) extends WithFilter[(K, V), CC1](filtered, iterableFactory) {

def map[K2, V2](f: ((K, V)) => (K2, V2)): CC2[K2, V2] = mapFactory.fromIterable(View.Map(filtered, f))

def flatMap[K2, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC2[K2, V2] = mapFactory.fromIterable(View.FlatMap(filtered, f))

override def withFilter(p: ((K, V)) => Boolean): MapWithFilter[K, V, CC1, CC2] =
new MapWithFilter(filtered.filter(p), iterableFactory, mapFactory)

}

class SortedMapWithFilter[K, +V, +CC1[_], +CC2[X, Y] <: Map[X, Y], +CC3[_, _]](
filtered: View[(K, V)],
iterableFactory: IterableFactory[CC1],
mapFactory: MapFactory[CC2],
sortedMapFactory: SortedMapFactory[CC3]
) extends MapWithFilter[K, V, CC1, CC2](filtered, iterableFactory, mapFactory) {

def map[K2 : Ordering, V2](f: ((K, V)) => (K2, V2)): CC3[K2, V2] =
sortedMapFactory.sortedFromIterable(View.Map(filtered, f))

def flatMap[K2 : Ordering, V2](f: ((K, V)) => IterableOnce[(K2, V2)]): CC3[K2, V2] =
sortedMapFactory.sortedFromIterable(View.FlatMap(filtered, f))

override def withFilter(p: ((K, V)) => Boolean): SortedMapWithFilter[K, V, CC1, CC2, CC3] =
new SortedMapWithFilter(filtered.filter(p), iterableFactory, mapFactory, sortedMapFactory)

}
2 changes: 1 addition & 1 deletion src/main/scala/strawman/collection/mutable/SortedSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ trait SortedSet[A]
with collection.SortedSet[A]
with SortedSetOps[A, SortedSet, SortedSet[A]]

trait SortedSetOps[A, +CC[X], +C <: SortedSet[A]]
trait SortedSetOps[A, +CC[X] <: SortedSet[X], +C <: SortedSet[A]]
extends SetOps[A, Set, C]
with collection.SortedSetOps[A, CC, C]

Expand Down
58 changes: 58 additions & 0 deletions src/test/scala/strawman/collection/test/WithFilterTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package strawman
package collection
package test

import immutable.{List, TreeSet, TreeMap}

import scala.{Char, Int, Unit}
import scala.Predef.{assert, ArrowAssoc}

import org.junit.Test

class WithFilterTest {

@Test
def iterables(): Unit = {
val xs1 =
for {
x <- List(1, 2, 3)
if x % 2 == 0
} yield x + 1
val xs1t: List[Int] = xs1
assert(xs1 == List(3))
}

@Test
def maps(): Unit = {
val xs1 =
for {
(k, v) <- Map(1 -> 'a', 2 -> 'b', 3 -> 'c')
if k % 2 == 0
} yield (v, k)
val xs1t: Map[Char, Int] = xs1
assert(xs1 == Map('b' -> 2))
}

@Test
def sorted(): Unit = {
val xs1 =
for {
x <- TreeSet(1, 2, 3)
if x % 2 == 0
} yield x + 1
val xs1t: TreeSet[Int] = xs1
assert(xs1 == TreeSet(3))
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This test fails on Dotty. This is probably something related to hashcodes. We observed a weird behavior here too. I’ll create an issue in Dotty.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

On Dotty xs1 == TreeSet().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually that’s not related to hashcodes, but maybe to inlining in Dotty. I added a commit that un-inlines some methods and now the tests pass on Dotty. I’m trying to minimize the problem.

}

@Test
def sortedMap(): Unit = {
val xs1 =
for {
(k, v) <- TreeMap(1 -> 'a', 2 -> 'b', 3 -> 'c')
if k % 2 == 0
} yield (v, k)
val xs1t: TreeMap[Char, Int] = xs1
assert(xs1 == TreeMap('b' -> 2))
}

}