diff --git a/core/shared/src/main/scala/monocle/Iso.scala b/core/shared/src/main/scala/monocle/Iso.scala index 94e88f295..ec41a2bf8 100644 --- a/core/shared/src/main/scala/monocle/Iso.scala +++ b/core/shared/src/main/scala/monocle/Iso.scala @@ -336,6 +336,10 @@ object Iso { /** alias for [[PIso]] id when S = T and A = B */ def id[S]: Iso[S, S] = PIso.id[S, S] + + /** create an [[Iso]] from a function that is its own inverse */ + def involuted[A](update: A => A): Iso[A, A] = + Iso(update)(update) } sealed abstract class IsoInstances { diff --git a/test/shared/src/test/scala/monocle/IsoSpec.scala b/test/shared/src/test/scala/monocle/IsoSpec.scala index bb115224d..ca2bc728c 100644 --- a/test/shared/src/test/scala/monocle/IsoSpec.scala +++ b/test/shared/src/test/scala/monocle/IsoSpec.scala @@ -39,6 +39,9 @@ class IsoSpec extends MonocleSuite { implicit def emptyCaseTypeEq[A] = Eq.fromUniversalEquals[EmptyCaseType[A]] val iso = Iso[IntWrapper, Int](_.i)(IntWrapper.apply) + val involutedListReverse = + Iso.involuted[List[Int]](_.reverse) // ∀ {T} -> List(ts: T*).reverse.reverse == List(ts: T*) + val involutedTwoMinusN = Iso.involuted[Int](2 - _) // ∀ {n : Int} -> n == 2 - (2 - n) checkAll("apply Iso", IsoTests(iso)) checkAll("GenIso", IsoTests(GenIso[IntWrapper, Int])) @@ -49,6 +52,9 @@ class IsoSpec extends MonocleSuite { checkAll("Iso id", IsoTests(Iso.id[Int])) + checkAll("Iso involutedListReverse", IsoTests(involutedListReverse)) + checkAll("Iso involutedTwoMinusN", IsoTests(involutedTwoMinusN)) + checkAll("Iso.asLens", LensTests(iso.asLens)) checkAll("Iso.asPrism", PrismTests(iso.asPrism)) checkAll("Iso.asOptional", OptionalTests(iso.asOptional)) @@ -122,6 +128,20 @@ class IsoSpec extends MonocleSuite { iso.modify(_ + 1)(IntWrapper(0)) shouldEqual IntWrapper(1) } + test("involuted") { + involutedListReverse.get(List(1, 2, 3)) shouldEqual List(3, 2, 1) + involutedListReverse.reverseGet(List(1, 2, 3)) shouldEqual List(3, 2, 1) + + involutedListReverse.reverse.get(List(1, 2, 3)) shouldEqual involutedListReverse.get(List(1, 2, 3)) + involutedListReverse.reverse.reverseGet(List(1, 2, 3)) shouldEqual involutedListReverse.reverseGet(List(1, 2, 3)) + + involutedTwoMinusN.get(5) shouldEqual -3 + involutedTwoMinusN.reverseGet(5) shouldEqual -3 + + involutedTwoMinusN.reverse.get(5) shouldEqual involutedTwoMinusN.get(5) + involutedTwoMinusN.reverse.reverseGet(5) shouldEqual involutedTwoMinusN.reverseGet(5) + } + test("GenIso nullary equality") { GenIso.unit[Nullary] shouldEqual _nullary }