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 filter, deprecate unsafeSelect #988

Merged
merged 2 commits into from
Dec 11, 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
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/monocle/Fold.scala
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ abstract class Fold[S, A] extends Serializable { self =>
def each[C](implicit evEach: Each[A, C]): Fold[S, C] =
composeTraversal(evEach.each)

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Fold[S, A] =
self.andThen(Optional.filter(predicate))

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

Expand Down
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/monocle/Getter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ abstract class Getter[S, A] extends Serializable { self =>
def each[C](implicit evEach: Each[A, C]): Fold[S, C] =
composeTraversal(evEach.each)

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Fold[S, A] =
self.andThen(Optional.filter(predicate))

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

Expand Down
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/monocle/Iso.scala
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,12 @@ final case class IsoSyntax[S, A](private val self: Iso[S, A]) extends AnyVal {
def each[C](implicit evEach: Each[A, C]): Traversal[S, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(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
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/monocle/Lens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,12 @@ final case class LensSyntax[S, A](private val self: Lens[S, A]) extends AnyVal {
def each[C](implicit evEach: Each[A, C]): Traversal[S, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(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
33 changes: 33 additions & 0 deletions core/shared/src/main/scala/monocle/Optional.scala
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,33 @@ object Optional {
def void[S, A]: Optional[S, A] =
Optional[S, A](_ => None)(_ => identity)

/** Select all the elements which satisfies the predicate.
* {{{
* val positiveNumbers = Traversal.fromTraverse[List, Int] composeOptional filter[Int](_ >= 0)
*
* positiveNumbers.getAll(List(1,2,-3,4,-5)) == List(1,2,4)
* positiveNumbers.modify(_ * 10)(List(1,2,-3,4,-5)) == List(10,20,-3,40,-5)
* }}}
*
* `filter` can break the fusion property, if `replace` or `modify` do not preserve the predicate.
* For example, here the first `modify` (`x - 3`) transform the positive number 1 into the
* negative number -2.
* {{{
* val positiveNumbers = Traversal.fromTraverse[List, Int] composeOptional Optional.filter[Int](_ >= 0)
* val list = List(1, 5, -3)
* val firstStep = positiveNumbers.modify(_ - 3)(list) // List(-2, 2, -3)
* val secondStep = positiveNumbers.modify(_ * 2)(firstStep) // List(-2, 4, -3)
* val bothSteps = positiveNumbers.modify(x => (x - 3) * 2)(list) // List(-4, 4, -3)
* secondStep != bothSteps
* }}}
*
* @see This method is called `filtered` in Haskell Lens.
*/
def filter[A](predicate: A => Boolean): Optional[A, A] =
Optional[A, A](value => if (predicate(value)) Some(value) else None)(newValue =>
current => if (predicate(current)) newValue else current
)

/** alias for [[POptional]] apply restricted to monomorphic update */
def apply[S, A](_getOption: S => Option[A])(_set: A => S => S): Optional[S, A] =
new Optional[S, A] {
Expand Down Expand Up @@ -338,6 +365,12 @@ final case class OptionalSyntax[S, A](private val self: Optional[S, A]) extends
def each[C](implicit evEach: Each[A, C]): Traversal[S, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(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
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/monocle/Prism.scala
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,12 @@ final case class PrismSyntax[S, A](private val self: Prism[S, A]) extends AnyVal
def each[C](implicit evEach: Each[A, C]): Traversal[S, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Optional[S, A] =
self.andThen(Optional.filter(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
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/monocle/Setter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,12 @@ final case class SetterSyntax[S, A](private val self: Setter[S, A]) extends AnyV
def each[C](implicit evEach: Each[A, C]): Setter[S, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Setter[S, A] =
self.andThen(Optional.filter(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
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/monocle/Traversal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,12 @@ final case class TraversalSyntax[S, A](private val self: Traversal[S, A]) extend
def each[C](implicit evEach: Each[A, C]): Traversal[S, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): Traversal[S, A] =
self.andThen(Optional.filter(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
8 changes: 7 additions & 1 deletion core/shared/src/main/scala/monocle/syntax/ApplyFold.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package monocle.syntax

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

case class ApplyFold[S, A](s: S, _fold: Fold[S, A]) {
def foldMap[M: Monoid](f: A => M): M = _fold.foldMap(f)(s)
Expand All @@ -20,6 +20,12 @@ case class ApplyFold[S, A](s: S, _fold: Fold[S, A]) {
def each[C](implicit evEach: Each[A, C]): ApplyFold[S, C] =
composeTraversal(evEach.each)

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplyFold[S, A] =
andThen(Optional.filter(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
Expand Up @@ -2,7 +2,7 @@ package monocle.syntax

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

final case class ApplyGetter[S, A](s: S, getter: Getter[S, A]) {
def get: A = getter.get(s)
Expand All @@ -12,6 +12,12 @@ final case class ApplyGetter[S, A](s: S, getter: Getter[S, A]) {
def each[C](implicit evEach: Each[A, C]): ApplyFold[S, C] =
composeTraversal(evEach.each)

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplyFold[S, A] =
andThen(Optional.filter(predicate))

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

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

import cats.{Eq, Functor}
import monocle.function.{At, Each, Index}
import monocle.{std, Fold, Getter, PIso, PLens, POptional, PPrism, PSetter, PTraversal}
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]) {
def get: A = iso.get(s)
Expand Down Expand Up @@ -78,6 +78,12 @@ final case class ApplyIsoSyntax[S, A](private val self: ApplyIso[S, S, A, A]) ex
def each[C](implicit evEach: Each[A, C]): ApplyTraversal[S, S, C, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(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
8 changes: 7 additions & 1 deletion core/shared/src/main/scala/monocle/syntax/ApplyLens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package monocle.syntax

import cats.{Eq, Functor}
import monocle.function.{At, Each, Index}
import monocle.{std, Fold, Getter, PIso, PLens, POptional, PPrism, PSetter, PTraversal}
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]) {
def get: A = lens.get(s)
Expand Down Expand Up @@ -79,6 +79,12 @@ final case class ApplyLensSyntax[S, A](private val self: ApplyLens[S, S, A, A])
def each[C](implicit evEach: Each[A, C]): ApplyTraversal[S, S, C, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(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
Expand Up @@ -2,7 +2,7 @@ package monocle.syntax

import cats.{Applicative, Eq}
import monocle.function.{At, Each, Index}
import monocle.{std, Fold, PIso, PLens, POptional, PPrism, PSetter, PTraversal}
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]) {
def getOption: Option[A] = optional.getOption(s)
Expand Down Expand Up @@ -86,6 +86,12 @@ final case class ApplyOptionalSyntax[S, A](private val self: ApplyOptional[S, S,
def each[C](implicit evEach: Each[A, C]): ApplyTraversal[S, S, C, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(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
Expand Up @@ -2,7 +2,7 @@ package monocle.syntax

import cats.{Applicative, Eq}
import monocle.function.{At, Each, Index}
import monocle.{std, Fold, PIso, PLens, POptional, PPrism, PSetter, PTraversal}
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]) {
def getOption: Option[A] = prism.getOption(s)
Expand Down Expand Up @@ -84,6 +84,12 @@ final case class ApplyPrismSyntax[S, A](private val self: ApplyPrism[S, S, A, A]
def each[C](implicit evEach: Each[A, C]): ApplyTraversal[S, S, C, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplyOptional[S, S, A, A] =
self.andThen(Optional.filter(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
Expand Up @@ -2,7 +2,7 @@ package monocle.syntax

import cats.Eq
import monocle.function.{At, Each, Index}
import monocle.{std, PIso, PLens, POptional, PPrism, PSetter, PTraversal}
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]) {
def replace(b: B): T = setter.replace(b)(s)
Expand Down Expand Up @@ -69,6 +69,12 @@ final case class ApplySetterSyntax[S, A](private val self: ApplySetter[S, S, A,
def each[C](implicit evEach: Each[A, C]): ApplySetter[S, S, C, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplySetter[S, S, A, A] =
self.andThen(Optional.filter(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
Expand Up @@ -2,7 +2,7 @@ package monocle.syntax

import cats.{Applicative, Eq}
import monocle.function.{At, Each, Index}
import monocle.{std, Fold, PIso, PLens, POptional, PPrism, PSetter, PTraversal}
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]) {
def getAll: List[A] = traversal.getAll(s)
Expand Down Expand Up @@ -84,6 +84,12 @@ final case class ApplyTraversalSyntax[S, A](private val self: ApplyTraversal[S,
def each[C](implicit evEach: Each[A, C]): ApplyTraversal[S, S, C, C] =
self composeTraversal evEach.each

/** Select all the elements which satisfies the predicate.
* This combinator can break the fusion property see Optional.filter for more details.
*/
def filter(predicate: A => Boolean): ApplyTraversal[S, S, A, A] =
self.andThen(Optional.filter(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 @@ -126,6 +126,14 @@ class FoldSpec extends MonocleSuite {
assertEquals(numbers.applyFold(fold).each.getAll, List(1, 2, 3, 4))
}

test("filter") {
val numbers = List(1, 2, 3)
val fold = Fold.fromFoldable[List, Int]

assertEquals(fold.filter(_ > 1).getAll(numbers), List(2, 3))
assertEquals(numbers.applyFold(fold).filter(_ > 1).getAll, List(2, 3))
}

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 @@ -109,6 +109,16 @@ class GetterSpec extends MonocleSuite {
assertEquals(obj.applyGetter(getter).each.getAll, List(1, 2, 3))
}

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

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

assertEquals(getter.filter(_ > 0).getAll(obj), List(2))
assertEquals(obj.applyGetter(getter).filter(_ > 0).getAll, List(2))
}

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

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

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

assertEquals(iso.filter(_ > 0).getOption(obj), Some(2))
assertEquals(obj.applyIso(iso).filter(_ > 0).getOption, Some(2))
}

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 @@ -130,6 +130,16 @@ class LensSpec extends MonocleSuite {
assertEquals(obj.applyLens(lens).each.getAll, List(1, 2, 3))
}

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

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

assertEquals(lens.filter(_ > 0).getOption(obj), Some(2))
assertEquals(obj.applyLens(lens).filter(_ > 0).getOption, Some(2))
}

test("at") {
val tuple2 = (1, 2)
val tuple2Lens = Lens.id[(Int, Int)]
Expand Down
Loading