diff --git a/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala b/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala index 0043e47..5fc30cf 100644 --- a/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala +++ b/quicklens/src/main/scala-3/com/softwaremill/quicklens/package.scala @@ -133,12 +133,8 @@ package object quicklens { } object QuicklensFunctor { - given QuicklensFunctor[List] with { - def map[A, B](fa: List[A], f: A => B): List[B] = fa.map(f) - } - - given QuicklensFunctor[Seq] with { - def map[A, B](fa: Seq[A], f: A => B): Seq[B] = fa.map(f) + given [S <: ([V] =>> Seq[V])]: QuicklensFunctor[S] with { + def map[A, B](fa: S[A], f: A => B): S[B] = fa.map(f).asInstanceOf[S[B]] } given QuicklensFunctor[Option] with { @@ -163,14 +159,15 @@ package object quicklens { } object QuicklensIndexedFunctor { - given QuicklensIndexedFunctor[List, Int] with { - def at[A](fa: List[A], f: A => A, idx: Int): List[A] = - fa.updated(idx, f(fa(idx))) - def atOrElse[A](fa: List[A], f: A => A, idx: Int, default: => A): List[A] = - fa.updated(idx, f(fa.applyOrElse(idx, Function.const(default)))) - def index[A](fa: List[A], f: A => A, idx: Int): List[A] = - if fa.isDefinedAt(idx) then fa.updated(idx, f(fa(idx))) else fa + given [S <: ([V] =>> Seq[V])]: QuicklensIndexedFunctor[S, Int] with { + def at[A](fa: S[A], f: A => A, idx: Int): S[A] = + fa.updated(idx, f(fa(idx))).asInstanceOf[S[A]] + def atOrElse[A](fa: S[A], f: A => A, idx: Int, default: => A): S[A] = + fa.updated(idx, f(fa.applyOrElse(idx, Function.const(default)))).asInstanceOf[S[A]] + def index[A](fa: S[A], f: A => A, idx: Int): S[A] = + if fa.isDefinedAt(idx) then fa.updated(idx, f(fa(idx))).asInstanceOf[S[A]] else fa } + given [K, M <: ([V] =>> Map[K, V])]: QuicklensIndexedFunctor[M, K] with { def at[A](fa: M[A], f: A => A, idx: K): M[A] = fa.updated(idx, f(fa(idx))).asInstanceOf[M[A]] diff --git a/quicklens/src/test/scala/com/softwaremill/quicklens/ModifyIndexedSeqIndexTest.scala b/quicklens/src/test/scala/com/softwaremill/quicklens/ModifyIndexedSeqIndexTest.scala new file mode 100644 index 0000000..a2220c9 --- /dev/null +++ b/quicklens/src/test/scala/com/softwaremill/quicklens/ModifyIndexedSeqIndexTest.scala @@ -0,0 +1,27 @@ +package com.softwaremill.quicklens + +import com.softwaremill.quicklens.TestData._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ModifyIndexedSeqIndexTest extends AnyFlatSpec with Matchers { + + it should "modify a non-nested indexed seq with case class item" in { + modify(is1)(_.index(2).a4.a5.name).using(duplicate) should be(l1at2dup) + modify(is1)(_.index(2)) + .using(a3 => modify(a3)(_.a4.a5.name).using(duplicate)) should be(l1at2dup) + } + + it should "modify a nested indexed seq using index" in { + modify(iss1)(_.index(2).index(1).name).using(duplicate) should be(ll1at2at1dup) + } + + it should "modify a nested indexed seq using index and each" in { + modify(iss1)(_.index(2).each.name).using(duplicate) should be(ll1at2eachdup) + modify(iss1)(_.each.index(1).name).using(duplicate) should be(ll1eachat1dup) + } + + it should "not modify if given index does not exist" in { + modify(is1)(_.index(10).a4.a5.name).using(duplicate) should be(l1) + } +} diff --git a/quicklens/src/test/scala/com/softwaremill/quicklens/ModifySeqIndexTest.scala b/quicklens/src/test/scala/com/softwaremill/quicklens/ModifySeqIndexTest.scala new file mode 100644 index 0000000..fbaaeb3 --- /dev/null +++ b/quicklens/src/test/scala/com/softwaremill/quicklens/ModifySeqIndexTest.scala @@ -0,0 +1,27 @@ +package com.softwaremill.quicklens + +import com.softwaremill.quicklens.TestData._ +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class ModifySeqIndexTest extends AnyFlatSpec with Matchers { + + it should "modify a non-nested seq with case class item" in { + modify(s1)(_.index(2).a4.a5.name).using(duplicate) should be(l1at2dup) + modify(s1)(_.index(2)) + .using(a3 => modify(a3)(_.a4.a5.name).using(duplicate)) should be(l1at2dup) + } + + it should "modify a nested seq using index" in { + modify(ss1)(_.index(2).index(1).name).using(duplicate) should be(ll1at2at1dup) + } + + it should "modify a nested seq using index and each" in { + modify(ss1)(_.index(2).each.name).using(duplicate) should be(ll1at2eachdup) + modify(ss1)(_.each.index(1).name).using(duplicate) should be(ll1eachat1dup) + } + + it should "not modify if given index does not exist" in { + modify(s1)(_.index(10).a4.a5.name).using(duplicate) should be(l1) + } +} diff --git a/quicklens/src/test/scala/com/softwaremill/quicklens/TestData.scala b/quicklens/src/test/scala/com/softwaremill/quicklens/TestData.scala index 47529ad..9ced840 100644 --- a/quicklens/src/test/scala/com/softwaremill/quicklens/TestData.scala +++ b/quicklens/src/test/scala/com/softwaremill/quicklens/TestData.scala @@ -63,6 +63,13 @@ object TestData { val ll1at2eachdup = List(List(A5("d1"), A5("d2")), List(A5("d3"), A5("d4"), A5("d5")), List(A5("d6d6"), A5("d7d7"))) val ll1eachat1dup = List(List(A5("d1"), A5("d2d2")), List(A5("d3"), A5("d4d4"), A5("d5")), List(A5("d6"), A5("d7d7"))) + + val s1: Seq[A3] = l1 + val ss1: Seq[Seq[A5]] = ll1 + + val is1 = l1.toIndexedSeq + val iss1 = ss1.map(_.toIndexedSeq).toIndexedSeq + case class M2(m3: Map[String, A4]) val m1 = Map("K1" -> A4(A5("d1")), "K2" -> A4(A5("d2")), "K3" -> A4(A5("d3")))