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

add short cut for filterIndex #999

Merged
merged 2 commits into from
Dec 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Fold.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cats.arrow.Choice
import cats.instances.int._
import cats.instances.list._
import cats.syntax.either._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.internal.Monoids

/** A [[Fold]] can be seen as a [[Getter]] with many targets or
Expand Down Expand Up @@ -97,6 +97,9 @@ abstract class Fold[S, A] extends Serializable { self =>
def filter(predicate: A => Boolean): Fold[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Fold[S, A1] =
self.andThen(ev.filterIndex(predicate))

def some[A1](implicit ev1: A =:= Option[A1]): Fold[S, A1] =
adapt[Option[A1]] composePrism (std.option.pSome)

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Getter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package monocle
import cats.{Eq, Monoid, Semigroupal}
import cats.arrow.{Arrow, Choice}
import cats.implicits._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}

/** A [[Getter]] can be seen as a glorified get method between
* a type S and a type A.
Expand Down Expand Up @@ -62,6 +62,9 @@ abstract class Getter[S, A] extends Serializable { self =>
def filter(predicate: A => Boolean): Fold[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Fold[S, A1] =
self.andThen(ev.filterIndex(predicate))

def some[A1](implicit ev1: A =:= Option[A1]): Fold[S, A1] =
adapt[Option[A1]] composePrism (std.option.pSome)

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Iso.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cats.{Applicative, Eq, Functor, Monoid}
import cats.arrow.Category
import cats.evidence.{<~<, Is}
import cats.syntax.either._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}

/** [[Iso]] is a type alias for [[PIso]] where `S` = `A` and `T` = `B`:
* {{{
Expand Down Expand Up @@ -417,6 +417,9 @@ final case class IsoSyntax[S, A](private val self: Iso[S, A]) extends AnyVal {
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Traversal[S, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): Iso[S, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Lens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package monocle
import cats.{Applicative, Eq, Functor, Monoid}
import cats.arrow.Choice
import cats.syntax.either._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}

/** A [[PLens]] can be seen as a pair of functions:
* - `get: S => A` i.e. from an `S`, we can extract an `A`
Expand Down Expand Up @@ -328,6 +328,9 @@ final case class LensSyntax[S, A](private val self: Lens[S, A]) extends AnyVal {
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Traversal[S, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): Lens[S, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Optional.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package monocle
import cats.{Applicative, Eq, Monoid}
import cats.arrow.Choice
import cats.syntax.either._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}

/** A [[POptional]] can be seen as a pair of functions:
* - `getOrModify: S => Either[T, A]`
Expand Down Expand Up @@ -371,6 +371,9 @@ final case class OptionalSyntax[S, A](private val self: Optional[S, A]) extends
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Traversal[S, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): Optional[S, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Prism.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cats.arrow.Category
import cats.evidence.{<~<, Is}
import cats.instances.option._
import cats.syntax.either._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}

/** A [[PPrism]] can be seen as a pair of functions:
* - `getOrModify: S => Either[T, A]`
Expand Down Expand Up @@ -383,6 +383,9 @@ final case class PrismSyntax[S, A](private val self: Prism[S, A]) extends AnyVal
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Traversal[S, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): Prism[S, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Setter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import cats.{Contravariant, Eq, Functor}
import cats.arrow.Choice
import cats.arrow.Profunctor
import cats.syntax.either._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}

/** A [[PSetter]] is a generalisation of Functor map:
* - `map: (A => B) => F[A] => F[B]`
Expand Down Expand Up @@ -207,6 +207,9 @@ final case class SetterSyntax[S, A](private val self: Setter[S, A]) extends AnyV
def filter(predicate: A => Boolean): Setter[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Setter[S, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): Setter[S, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/Traversal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import cats.data.Const
import cats.instances.int._
import cats.instances.list._
import cats.syntax.either._
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.internal.Monoids

/** A [[PTraversal]] can be seen as a [[POptional]] generalised to 0 to n targets
Expand Down Expand Up @@ -354,6 +354,9 @@ final case class TraversalSyntax[S, A](private val self: Traversal[S, A]) extend
def filter(predicate: A => Boolean): Traversal[S, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Traversal[S, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): Traversal[S, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/syntax/ApplyFold.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.{Eq, Monoid}
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Fold, Getter, Optional, PIso, PLens, POptional, PPrism, PTraversal}

case class ApplyFold[S, A](s: S, _fold: Fold[S, A]) {
Expand All @@ -26,6 +26,9 @@ case class ApplyFold[S, A](s: S, _fold: Fold[S, A]) {
def filter(predicate: A => Boolean): ApplyFold[S, A] =
andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplyFold[S, A1] =
andThen(ev.filterIndex(predicate))

def some[A1](implicit ev1: A =:= Option[A1]): ApplyFold[S, A1] =
adapt[Option[A1]] composePrism (std.option.pSome)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.Eq
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Fold, Getter, Optional, PIso, PLens, POptional, PPrism, PTraversal}

final case class ApplyGetter[S, A](s: S, getter: Getter[S, A]) {
Expand All @@ -18,6 +18,9 @@ final case class ApplyGetter[S, A](s: S, getter: Getter[S, A]) {
def filter(predicate: A => Boolean): ApplyFold[S, A] =
andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplyFold[S, A1] =
andThen(ev.filterIndex(predicate))

def some[A1](implicit ev1: A =:= Option[A1]): ApplyFold[S, A1] =
adapt[Option[A1]] composePrism (std.option.pSome)

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/syntax/ApplyIso.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.{Eq, Functor}
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Fold, Getter, Optional, PIso, PLens, POptional, PPrism, PSetter, PTraversal}

final case class ApplyIso[S, T, A, B](s: S, iso: PIso[S, T, A, B]) {
Expand Down Expand Up @@ -84,6 +84,9 @@ final case class ApplyIsoSyntax[S, A](private val self: ApplyIso[S, S, A, A]) ex
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplyTraversal[S, S, A1, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): ApplyIso[S, S, A1, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
5 changes: 4 additions & 1 deletion core/shared/src/main/scala/monocle/syntax/ApplyLens.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.{Eq, Functor}
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Fold, Getter, Optional, PIso, PLens, POptional, PPrism, PSetter, PTraversal}

final case class ApplyLens[S, T, A, B](s: S, lens: PLens[S, T, A, B]) {
Expand Down Expand Up @@ -85,6 +85,9 @@ final case class ApplyLensSyntax[S, A](private val self: ApplyLens[S, S, A, A])
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplyTraversal[S, S, A1, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): ApplyLens[S, S, A1, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.{Applicative, Eq}
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Fold, Optional, PIso, PLens, POptional, PPrism, PSetter, PTraversal}

final case class ApplyOptional[S, T, A, B](s: S, optional: POptional[S, T, A, B]) {
Expand Down Expand Up @@ -92,6 +92,9 @@ final case class ApplyOptionalSyntax[S, A](private val self: ApplyOptional[S, S,
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplyTraversal[S, S, A1, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): ApplyOptional[S, S, A1, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.{Applicative, Eq}
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Fold, Optional, PIso, PLens, POptional, PPrism, PSetter, PTraversal}

final case class ApplyPrism[S, T, A, B](s: S, prism: PPrism[S, T, A, B]) {
Expand Down Expand Up @@ -90,6 +90,9 @@ final case class ApplyPrismSyntax[S, A](private val self: ApplyPrism[S, S, A, A]
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplyTraversal[S, S, A1, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): ApplyPrism[S, S, A1, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.Eq
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Optional, PIso, PLens, POptional, PPrism, PSetter, PTraversal}

final case class ApplySetter[S, T, A, B](s: S, setter: PSetter[S, T, A, B]) {
Expand Down Expand Up @@ -75,6 +75,9 @@ final case class ApplySetterSyntax[S, A](private val self: ApplySetter[S, S, A,
def filter(predicate: A => Boolean): ApplySetter[S, S, A, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplySetter[S, S, A1, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): ApplySetter[S, S, A1, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package monocle.syntax

import cats.{Applicative, Eq}
import monocle.function.{At, Each, Index}
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.{std, Fold, Optional, PIso, PLens, POptional, PPrism, PSetter, PTraversal}

final case class ApplyTraversal[S, T, A, B](s: S, traversal: PTraversal[S, T, A, B]) {
Expand Down Expand Up @@ -90,6 +90,9 @@ final case class ApplyTraversalSyntax[S, A](private val self: ApplyTraversal[S,
def filter(predicate: A => Boolean): ApplyTraversal[S, S, A, A] =
self.andThen(Optional.filter(predicate))

def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): ApplyTraversal[S, S, A1, A1] =
self.andThen(ev.filterIndex(predicate))

def withDefault[A1: Eq](defaultValue: A1)(implicit evOpt: A =:= Option[A1]): ApplyTraversal[S, S, A1, A1] =
self.adapt[Option[A1], Option[A1]] composeIso (std.option.withDefault(defaultValue))

Expand Down
8 changes: 8 additions & 0 deletions test/shared/src/test/scala/monocle/FoldSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,14 @@ class FoldSpec extends MonocleSuite {
assertEquals(numbers.applyFold(fold).filter(_ > 1).getAll, List(2, 3))
}

test("filterIndex") {
val words = List(List("hello", "world"), List("hey", "hi"))
val fold = Fold.fromFoldable[List, List[String]]

assertEquals(fold.filterIndex((_: Int) > 0).getAll(words), List("world", "hi"))
assertEquals(words.applyFold(fold).filterIndex((_: Int) > 0).getAll, List("world", "hi"))
}

test("at") {
val tuple2 = (1, 2)
val tuple2Fold = Fold.id[(Int, Int)]
Expand Down
10 changes: 10 additions & 0 deletions test/shared/src/test/scala/monocle/GetterSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@ class GetterSpec extends MonocleSuite {
assertEquals(obj.applyGetter(getter).filter(_ > 0).getAll, List(2))
}

test("filterIndex") {
case class SomeTest(x: Int, y: List[String])
val obj = SomeTest(1, List("hello", "world"))

val getter = Getter[SomeTest, List[String]](_.y)

assertEquals(getter.filterIndex((_: Int) > 0).getAll(obj), List("world"))
assertEquals(obj.applyGetter(getter).filterIndex((_: Int) > 0).getAll, List("world"))
}

test("at") {
val tuple2 = (1, 2)
val tuple2Getter = Getter.id[(Int, Int)]
Expand Down
12 changes: 11 additions & 1 deletion test/shared/src/test/scala/monocle/IsoSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ assertEquals( (Nullary() match { case _nullary(unit) => unit }) , (()))
assertEquals(obj.applyIso(iso).each.getAll, List(1, 2, 3))
}

test("each") {
test("filter") {
case class SomeTest(y: Int)
val obj = SomeTest(2)

Expand All @@ -211,6 +211,16 @@ assertEquals( (Nullary() match { case _nullary(unit) => unit }) , (()))
assertEquals(obj.applyIso(iso).filter(_ > 0).getOption, Some(2))
}

test("filterIndex") {
case class SomeTest(y: List[String])
val obj = SomeTest(List("hello", "world"))

val iso = Iso[SomeTest, List[String]](_.y)(SomeTest)

assertEquals(iso.filterIndex((_: Int) > 0).getAll(obj), List("world"))
assertEquals(obj.applyIso(iso).filterIndex((_: Int) > 0).getAll, List("world"))
}

test("at") {
val tuple2 = (1, 2)
val tuple2Lens = Iso.id[(Int, Int)]
Expand Down
10 changes: 10 additions & 0 deletions test/shared/src/test/scala/monocle/LensSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,16 @@ class LensSpec extends MonocleSuite {
assertEquals(obj.applyLens(lens).filter(_ > 0).getOption, Some(2))
}

test("filterIndex") {
case class SomeTest(x: Int, y: List[String])
val obj = SomeTest(1, List("hello", "world"))

val lens = GenLens[SomeTest](_.y)

assertEquals(lens.filterIndex((_: Int) > 0).getAll(obj), List("world"))
assertEquals(obj.applyLens(lens).filterIndex((_: Int) > 0).getAll, List("world"))
}

test("at") {
val tuple2 = (1, 2)
val tuple2Lens = Lens.id[(Int, Int)]
Expand Down
10 changes: 10 additions & 0 deletions test/shared/src/test/scala/monocle/OptionalSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,16 @@ class OptionalSpec extends MonocleSuite {
assertEquals(positiveNumbers.modify(_ * 10)(List(1, 2, -3, 4, -5)), List(10, 20, -3, 40, -5))
}

test("filterIndex") {
case class SomeTest(x: Int, y: List[String])
val obj = SomeTest(1, List("hello", "world"))

val optional = GenLens[SomeTest](_.y).asOptional

assertEquals(optional.filterIndex((_: Int) > 0).getAll(obj), List("world"))
assertEquals(obj.applyOptional(optional).filterIndex((_: Int) > 0).getAll, List("world"))
}

test("filter can break the fusion property") {
val positiveNumbers = Traversal.fromTraverse[List, Int] composeOptional Optional.filter[Int](_ >= 0)
val list = List(1, 5, -3)
Expand Down
Loading