From 4cee372f1a63e41383beeb6c4d8232044aa1fe0d Mon Sep 17 00:00:00 2001 From: danicheg Date: Fri, 23 Aug 2024 11:26:05 +0300 Subject: [PATCH 01/11] Add the Transformer#contramap --- .../io/scalaland/chimney/Transformer.scala | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala index d6863c8cb..ca0049285 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala @@ -18,7 +18,7 @@ import io.scalaland.chimney.internal.runtime.{TransformerFlags, TransformerOverr * @since 0.1.0 */ @FunctionalInterface -trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { +trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { self => /** Run transformation using provided value as a source. * @@ -30,6 +30,28 @@ trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { * @since 0.1.0 */ def transform(src: From): To + + /** Creates a new [[io.scalaland.chimney.Transformer Transformer]] by applying a pure function to a source of type `A` + * before transforming it to `To`. See an example: + * {{{ + * val stringTransformer: Transformer[String, Int] = _.length + * + * case class Id(id: String) + * + * implicit val idTransformer: Transformer[Id, Int] = + * stringTransformer.contramap(_.id) + * }}} + * + * @param f + * a pure function to map a value of `A` to `From` + * @return + * new [[io.scalaland.chimney.Transformer Transformer]] + * + * @since 1.5.0 + */ + def contramap[A](f: A => From): Transformer[A, To] = new Transformer[A, To] { + override def transform(src: A): To = self.transform(f(src)) + } } /** Companion of [[io.scalaland.chimney.Transformer]]. From c1666542c44bd5c03c6ebc7421859a3269a9cd3b Mon Sep 17 00:00:00 2001 From: danicheg Date: Fri, 23 Aug 2024 11:26:27 +0300 Subject: [PATCH 02/11] Add tests for Transformer#contramap --- .../io/scalaland/chimney/Transformer.scala | 2 +- .../chimney/TotalTransformerSpec.scala | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala diff --git a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala index ca0049285..24c4947b1 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala @@ -49,7 +49,7 @@ trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { self => * * @since 1.5.0 */ - def contramap[A](f: A => From): Transformer[A, To] = new Transformer[A, To] { + final def contramap[A](f: A => From): Transformer[A, To] = new Transformer[A, To] { override def transform(src: A): To = self.transform(f(src)) } } diff --git a/chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala b/chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala new file mode 100644 index 000000000..9cb175e93 --- /dev/null +++ b/chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala @@ -0,0 +1,42 @@ +package io.scalaland.chimney + +import io.scalaland.chimney.dsl.* + +class TotalTransformerSpec extends ChimneySpec { + + test("contramap") { + case class Id(id: String) + + case class Length(length: Int) + + trait Prefix { + def value: String + } + + object Prefix { + case object FooPrefix extends Prefix { + override def value: String = "Foo" + } + + case object BarPrefix extends Prefix { + override def value: String = "Bar" + } + } + + val stringTransformer = new Transformer[String, Length] { + override def transform(src: String): Length = Length(src.length) + } + + implicit val idTransformer: Transformer[Id, Length] = + stringTransformer.contramap(_.id) + + implicit val prefixTransformer: Transformer[Prefix, Length] = + stringTransformer.contramap(_.value) + + val id = "id" + Id(id).into[Length].transform ==> Length(id.length) + + val prefix: Prefix = Prefix.FooPrefix + prefix.into[Length].transform ==> Length(prefix.value.length) + } +} From ac9126e9b347fdee8b051ce63283fc0c07e3467e Mon Sep 17 00:00:00 2001 From: danicheg Date: Fri, 23 Aug 2024 12:58:06 +0300 Subject: [PATCH 03/11] Add the PartialTransformer#map --- .../chimney/PartialTransformer.scala | 29 ++++++++++++++++++- .../io/scalaland/chimney/Transformer.scala | 2 +- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala index ab6605a74..e890c99a7 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala @@ -19,7 +19,7 @@ import io.scalaland.chimney.internal.runtime.{TransformerFlags, TransformerOverr * @since 0.7.0 */ @FunctionalInterface -trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, To] { +trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, To] { self => /** Run transformation using provided value as a source. * @@ -58,6 +58,33 @@ trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, */ final def transformFailFast(src: From): partial.Result[To] = transform(src, failFast = true) + + /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a + * [[io.scalaland.chimney.partial.Result Result]] of transforming `From` to `To`. See an example: + * {{{ + * val stringTransformer: PartialTransformer[String, Int] = + * PartialTransformer.fromFunction(_.length) + * + * case class Length(length: Int) + * + * implicit val toLengthTransformer: PartialTransformer[String, Length] = + * stringTransformer.map(id => Length(id)) + * }}} + * + * @param f + * a pure function that maps a value of `To` to `A` + * @return + * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] + * + * @since 1.5.0 + */ + final def map[A](f: To => A): PartialTransformer[From, A] = new PartialTransformer[From, A] { + override def transform(src: From, failFast: Boolean): partial.Result[A] = + self.transform(src, failFast) match { + case partial.Result.Value(to) => partial.Result.Value(f(to)) + case errs: partial.Result.Errors => errs.asInstanceOf[partial.Result[A]] + } + } } /** Companion of [[io.scalaland.chimney.PartialTransformer]]. diff --git a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala index 24c4947b1..ca4441104 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala @@ -43,7 +43,7 @@ trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { self => * }}} * * @param f - * a pure function to map a value of `A` to `From` + * a pure function that maps a value of `A` to `From` * @return * new [[io.scalaland.chimney.Transformer Transformer]] * From 2610f5403ddb5eb5ecd5878c4a1fd93f213447a6 Mon Sep 17 00:00:00 2001 From: danicheg Date: Fri, 23 Aug 2024 12:58:31 +0300 Subject: [PATCH 04/11] Add tests for PartialTransformer#map --- .../chimney/PartialTransformerSpec.scala | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala b/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala index 93a5a7a79..e4f6186a7 100644 --- a/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala +++ b/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala @@ -1,5 +1,7 @@ package io.scalaland.chimney +import io.scalaland.chimney.dsl.* + class PartialTransformerSpec extends ChimneySpec { private val pt1 = PartialTransformer[String, Int](str => partial.Result.fromValue(str.toInt)) @@ -73,4 +75,43 @@ class PartialTransformerSpec extends ChimneySpec { // no second error due to fail fast mode ) } + + test("map") { + case class Length(length: Int) + + trait Prefix { + def code: Int + } + + object Prefix { + + def from(i: Int): Prefix = i match { + case 1 => FooPrefix + case 2 => BarPrefix + case _ => NanPrefix + } + + case object NanPrefix extends Prefix { + override def code: Int = 0 + } + + case object FooPrefix extends Prefix { + override def code: Int = 1 + } + + case object BarPrefix extends Prefix { + override def code: Int = 2 + } + } + + implicit val toLengthTransformer: PartialTransformer[String, Length] = + pt1.map(Length.apply) + + implicit val toPrefixTransformer: PartialTransformer[String, Prefix] = + pt1.map(Prefix.from) + + val id = "2" + id.intoPartial[Length].transform ==> partial.Result.fromValue(Length(id.toInt)) + id.intoPartial[Prefix].transform ==> partial.Result.fromValue(Prefix.BarPrefix) + } } From 787e122ce060a4924a17f5827b3989cb71c839c6 Mon Sep 17 00:00:00 2001 From: danicheg Date: Fri, 23 Aug 2024 14:32:56 +0300 Subject: [PATCH 05/11] Use predefined contramap in catsContravariantForTransformer --- .../CatsPartialTransformerImplicits.scala | 19 +++++++++---------- .../cats/CatsTotalTransformerImplicits.scala | 17 ++++++++--------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsPartialTransformerImplicits.scala b/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsPartialTransformerImplicits.scala index 177846415..5c6ea4e96 100644 --- a/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsPartialTransformerImplicits.scala +++ b/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsPartialTransformerImplicits.scala @@ -51,17 +51,16 @@ trait CatsPartialTransformerImplicits { override def tailRecM[A, B](a: A)( f: A => PartialTransformer[Source, Either[A, B]] - ): PartialTransformer[Source, B] = - (src, failFast) => { - def run(a1: A) = partial.Result.fromCatching(f(a1).transform(src, failFast)).flatMap(identity) - @scala.annotation.tailrec - def loop(a1: A): partial.Result[B] = run(a1) match { - case partial.Result.Value(Left(a2)) => loop(a2) - case partial.Result.Value(Right(b)) => partial.Result.Value(b) - case errors => errors.asInstanceOf[partial.Result[B]] - } - loop(a) + ): PartialTransformer[Source, B] = { (src, failFast) => + def run(a1: A) = partial.Result.fromCatching(f(a1).transform(src, failFast)).flatMap(identity) + @scala.annotation.tailrec + def loop(a1: A): partial.Result[B] = run(a1) match { + case partial.Result.Value(Left(a2)) => loop(a2) + case partial.Result.Value(Right(b)) => partial.Result.Value(b) + case errors => errors.asInstanceOf[partial.Result[B]] } + loop(a) + } override def raiseError[A](e: partial.Result.Errors): PartialTransformer[Source, A] = (_, _) => e diff --git a/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsTotalTransformerImplicits.scala b/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsTotalTransformerImplicits.scala index b2b6eb4f3..78a534647 100644 --- a/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsTotalTransformerImplicits.scala +++ b/chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsTotalTransformerImplicits.scala @@ -39,15 +39,14 @@ trait CatsTotalTransformerImplicits { override def tailRecM[A, B](a: A)( f: A => Transformer[Source, Either[A, B]] - ): Transformer[Source, B] = - src => { - @scala.annotation.tailrec - def loop(a1: A): B = f(a1).transform(src) match { - case Left(a2) => loop(a2) - case Right(b) => b - } - loop(a) + ): Transformer[Source, B] = { src => + @scala.annotation.tailrec + def loop(a1: A): B = f(a1).transform(src) match { + case Left(a2) => loop(a2) + case Right(b) => b } + loop(a) + } override def coflatMap[A, B]( fa: Transformer[Source, A] @@ -60,6 +59,6 @@ trait CatsTotalTransformerImplicits { implicit final def catsContravariantForTransformer[Target]: Contravariant[Transformer[*, Target]] = new Contravariant[Transformer[*, Target]] { def contramap[A, B](fa: Transformer[A, Target])(f: B => A): Transformer[B, Target] = - b => fa.transform(f(b)) + fa.contramap(f) } } From a3eb1c34f568d379bbf5ac40ccee6e722b7f2877 Mon Sep 17 00:00:00 2001 From: danicheg Date: Sat, 31 Aug 2024 11:19:31 +0300 Subject: [PATCH 06/11] Add the Transformer#map --- .../io/scalaland/chimney/Transformer.scala | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala index ca4441104..18ddb50b2 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala @@ -52,6 +52,28 @@ trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { self => final def contramap[A](f: A => From): Transformer[A, To] = new Transformer[A, To] { override def transform(src: A): To = self.transform(f(src)) } + + /** Creates a new [[io.scalaland.chimney.Transformer Transformer]] by applying a pure function to a result of + * transforming `From` to `To`. See an example: + * {{{ + * val stringTransformer: Transformer[String, Int] = _.length + * + * case class Length(length: Int) + * + * implicit val toLengthTransformer: Transformer[String, Length] = + * stringTransformer.map(id => Length(id)) + * }}} + * + * @param f + * a pure function that maps a value of `To` to `A` + * @return + * new [[io.scalaland.chimney.Transformer Transformer]] + * + * @since 1.5.0 + */ + final def map[A](f: To => A): Transformer[From, A] = new Transformer[From, A] { + override def transform(src: From): A = f(self.transform(src)) + } } /** Companion of [[io.scalaland.chimney.Transformer]]. From 638901f3763e03cbaf113d040b34eed0ef8fe77a Mon Sep 17 00:00:00 2001 From: danicheg Date: Sat, 31 Aug 2024 11:19:59 +0300 Subject: [PATCH 07/11] Add the PartialTransformer#contramap --- .../chimney/PartialTransformer.scala | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala index e890c99a7..997a25f1f 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala @@ -85,6 +85,29 @@ trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, case errs: partial.Result.Errors => errs.asInstanceOf[partial.Result[A]] } } + + /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a + * source of type `A` before transforming it to `To`. See an example: + * {{{ + * val stringTransformer: PartialTransformer[String, Int] = + * PartialTransformer.fromFunction(_.length) + * + * case class Id(id: String) + * + * implicit val idTransformer: PartialTransformer[Id, Int] = + * stringTransformer.contramap(_.id) + * }}} + * + * @param f + * a pure function that maps a value of `A` to `From` + * @return + * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] + * @since 1.5.0 + */ + final def contramap[A](f: A => From): PartialTransformer[A, To] = new PartialTransformer[A, To] { + override def transform(src: A, failFast: Boolean): partial.Result[To] = + self.transform(f(src), failFast) + } } /** Companion of [[io.scalaland.chimney.PartialTransformer]]. From 4008c52e793918d7cb1e281f2b6ceb212e78ca7e Mon Sep 17 00:00:00 2001 From: danicheg Date: Sat, 31 Aug 2024 11:28:35 +0300 Subject: [PATCH 08/11] Move map/contramap to the distinct trait TransformerOps --- .../io/scalaland/chimney/Transformer.scala | 94 ++++++++++--------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala index 18ddb50b2..70592ae1a 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/Transformer.scala @@ -31,49 +31,6 @@ trait Transformer[From, To] extends Transformer.AutoDerived[From, To] { self => */ def transform(src: From): To - /** Creates a new [[io.scalaland.chimney.Transformer Transformer]] by applying a pure function to a source of type `A` - * before transforming it to `To`. See an example: - * {{{ - * val stringTransformer: Transformer[String, Int] = _.length - * - * case class Id(id: String) - * - * implicit val idTransformer: Transformer[Id, Int] = - * stringTransformer.contramap(_.id) - * }}} - * - * @param f - * a pure function that maps a value of `A` to `From` - * @return - * new [[io.scalaland.chimney.Transformer Transformer]] - * - * @since 1.5.0 - */ - final def contramap[A](f: A => From): Transformer[A, To] = new Transformer[A, To] { - override def transform(src: A): To = self.transform(f(src)) - } - - /** Creates a new [[io.scalaland.chimney.Transformer Transformer]] by applying a pure function to a result of - * transforming `From` to `To`. See an example: - * {{{ - * val stringTransformer: Transformer[String, Int] = _.length - * - * case class Length(length: Int) - * - * implicit val toLengthTransformer: Transformer[String, Length] = - * stringTransformer.map(id => Length(id)) - * }}} - * - * @param f - * a pure function that maps a value of `To` to `A` - * @return - * new [[io.scalaland.chimney.Transformer Transformer]] - * - * @since 1.5.0 - */ - final def map[A](f: To => A): Transformer[From, A] = new Transformer[From, A] { - override def transform(src: From): A = f(self.transform(src)) - } } /** Companion of [[io.scalaland.chimney.Transformer]]. @@ -140,12 +97,61 @@ object Transformer extends TransformerCompanionPlatform { * @since 0.8.0 */ @FunctionalInterface - trait AutoDerived[From, To] { + trait AutoDerived[From, To] extends TransformerOps[From, To] { def transform(src: From): To } /** @since 0.8.0 */ object AutoDerived extends TransformerAutoDerivedCompanionPlatform + + trait TransformerOps[From, To] { self => + + def transform(src: From): To + + /** Creates a new [[io.scalaland.chimney.Transformer Transformer]] by applying a pure function to a source of type + * `A` before transforming it to `To`. See an example: + * {{{ + * val stringTransformer: Transformer[String, Int] = _.length + * + * case class Id(id: String) + * + * implicit val idTransformer: Transformer[Id, Int] = + * stringTransformer.contramap(_.id) + * }}} + * + * @param f + * a pure function that maps a value of `A` to `From` + * @return + * new [[io.scalaland.chimney.Transformer Transformer]] + * + * @since 1.5.0 + */ + final def contramap[A](f: A => From): Transformer[A, To] = new Transformer[A, To] { + override def transform(src: A): To = self.transform(f(src)) + } + + /** Creates a new [[io.scalaland.chimney.Transformer Transformer]] by applying a pure function to a result of + * transforming `From` to `To`. See an example: + * {{{ + * val stringTransformer: Transformer[String, Int] = _.length + * + * case class Length(length: Int) + * + * implicit val toLengthTransformer: Transformer[String, Length] = + * stringTransformer.map(id => Length(id)) + * }}} + * + * @param f + * a pure function that maps a value of `To` to `A` + * @return + * new [[io.scalaland.chimney.Transformer Transformer]] + * + * @since 1.5.0 + */ + final def map[A](f: To => A): Transformer[From, A] = new Transformer[From, A] { + override def transform(src: From): A = f(self.transform(src)) + } + } } // extended by TransformerCompanionPlatform private[chimney] trait TransformerLowPriorityImplicits1 extends TransformerLowPriorityImplicits2 { From 64c486caee271431e840a6d61e2dcfbe171d0e7d Mon Sep 17 00:00:00 2001 From: danicheg Date: Sat, 31 Aug 2024 11:37:49 +0300 Subject: [PATCH 09/11] Move map/contramap to the distinct trait PartialTransformerOps --- .../chimney/PartialTransformer.scala | 107 ++++++++++-------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala index 997a25f1f..87aac8a4d 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala @@ -59,55 +59,6 @@ trait PartialTransformer[From, To] extends PartialTransformer.AutoDerived[From, final def transformFailFast(src: From): partial.Result[To] = transform(src, failFast = true) - /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a - * [[io.scalaland.chimney.partial.Result Result]] of transforming `From` to `To`. See an example: - * {{{ - * val stringTransformer: PartialTransformer[String, Int] = - * PartialTransformer.fromFunction(_.length) - * - * case class Length(length: Int) - * - * implicit val toLengthTransformer: PartialTransformer[String, Length] = - * stringTransformer.map(id => Length(id)) - * }}} - * - * @param f - * a pure function that maps a value of `To` to `A` - * @return - * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] - * - * @since 1.5.0 - */ - final def map[A](f: To => A): PartialTransformer[From, A] = new PartialTransformer[From, A] { - override def transform(src: From, failFast: Boolean): partial.Result[A] = - self.transform(src, failFast) match { - case partial.Result.Value(to) => partial.Result.Value(f(to)) - case errs: partial.Result.Errors => errs.asInstanceOf[partial.Result[A]] - } - } - - /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a - * source of type `A` before transforming it to `To`. See an example: - * {{{ - * val stringTransformer: PartialTransformer[String, Int] = - * PartialTransformer.fromFunction(_.length) - * - * case class Id(id: String) - * - * implicit val idTransformer: PartialTransformer[Id, Int] = - * stringTransformer.contramap(_.id) - * }}} - * - * @param f - * a pure function that maps a value of `A` to `From` - * @return - * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] - * @since 1.5.0 - */ - final def contramap[A](f: A => From): PartialTransformer[A, To] = new PartialTransformer[A, To] { - override def transform(src: A, failFast: Boolean): partial.Result[To] = - self.transform(f(src), failFast) - } } /** Companion of [[io.scalaland.chimney.PartialTransformer]]. @@ -213,7 +164,7 @@ object PartialTransformer extends PartialTransformerCompanionPlatform { * @since 0.8.0 */ @FunctionalInterface - trait AutoDerived[From, To] { + trait AutoDerived[From, To] extends PartialTransformerOps[From, To] { def transform(src: From, failFast: Boolean): partial.Result[To] } @@ -223,6 +174,62 @@ object PartialTransformer extends PartialTransformerCompanionPlatform { implicit def liftTotal[From, To](implicit total: Transformer[From, To]): AutoDerived[From, To] = (src: From, failFast: Boolean) => partial.Result.fromCatching(total.transform(src)) } + + trait PartialTransformerOps[From, To] { self => + + def transform(src: From, failFast: Boolean): partial.Result[To] + + /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a + * [[io.scalaland.chimney.partial.Result Result]] of transforming `From` to `To`. See an example: + * {{{ + * val stringTransformer: PartialTransformer[String, Int] = + * PartialTransformer.fromFunction(_.length) + * + * case class Length(length: Int) + * + * implicit val toLengthTransformer: PartialTransformer[String, Length] = + * stringTransformer.map(id => Length(id)) + * }}} + * + * @param f + * a pure function that maps a value of `To` to `A` + * @return + * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] + * + * @since 1.5.0 + */ + final def map[A](f: To => A): PartialTransformer[From, A] = new PartialTransformer[From, A] { + override def transform(src: From, failFast: Boolean): partial.Result[A] = + self.transform(src, failFast) match { + case partial.Result.Value(to) => partial.Result.Value(f(to)) + case errs: partial.Result.Errors => errs.asInstanceOf[partial.Result[A]] + } + } + + /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a + * source of type `A` before transforming it to `To`. See an example: + * {{{ + * val stringTransformer: PartialTransformer[String, Int] = + * PartialTransformer.fromFunction(_.length) + * + * case class Id(id: String) + * + * implicit val idTransformer: PartialTransformer[Id, Int] = + * stringTransformer.contramap(_.id) + * }}} + * + * @param f + * a pure function that maps a value of `A` to `From` + * @return + * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] + * + * @since 1.5.0 + */ + final def contramap[A](f: A => From): PartialTransformer[A, To] = new PartialTransformer[A, To] { + override def transform(src: A, failFast: Boolean): partial.Result[To] = + self.transform(f(src), failFast) + } + } } // extended by PartialTransformerCompanionPlatform private[chimney] trait PartialTransformerLowPriorityImplicits1 { this: PartialTransformer.type => From b6882c6fcfc38bc939bdcdfcc83330df259bf5f1 Mon Sep 17 00:00:00 2001 From: danicheg Date: Sat, 31 Aug 2024 11:52:24 +0300 Subject: [PATCH 10/11] Add tests for Transformer#map --- .../chimney/TotalTransformerSpec.scala | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala b/chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala index 9cb175e93..a23a1efc0 100644 --- a/chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala +++ b/chimney/src/test/scala/io/scalaland/chimney/TotalTransformerSpec.scala @@ -4,6 +4,49 @@ import io.scalaland.chimney.dsl.* class TotalTransformerSpec extends ChimneySpec { + test("map") { + case class Length(length: Int) + + trait Prefix { + def code: Int + } + + object Prefix { + + def from(i: Int): Prefix = i match { + case 1 => FooPrefix + case 2 => BarPrefix + case _ => NanPrefix + } + + case object NanPrefix extends Prefix { + override def code: Int = 0 + } + + case object FooPrefix extends Prefix { + override def code: Int = 1 + } + + case object BarPrefix extends Prefix { + override def code: Int = 2 + } + } + + val stringTransformer = new Transformer[String, Int] { + override def transform(src: String): Int = src.length + } + + implicit val toLengthTransformer: Transformer[String, Length] = + stringTransformer.map(Length.apply) + + implicit val toPrefixTransformer: Transformer[String, Prefix] = + stringTransformer.map(Prefix.from) + + val id = "1" + id.into[Length].transform ==> Length(id.length) + id.into[Prefix].transform ==> Prefix.FooPrefix + } + test("contramap") { case class Id(id: String) From 0733e9a6ec26c2d2df0e4332be0451eb76b1a8d3 Mon Sep 17 00:00:00 2001 From: danicheg Date: Sat, 31 Aug 2024 12:06:01 +0300 Subject: [PATCH 11/11] Add tests for PartialTransformer#contramap --- .../chimney/PartialTransformer.scala | 38 +++++++++---------- .../chimney/PartialTransformerSpec.scala | 30 +++++++++++++++ 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala index 87aac8a4d..9bfbfe938 100644 --- a/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala +++ b/chimney/src/main/scala/io/scalaland/chimney/PartialTransformer.scala @@ -180,54 +180,54 @@ object PartialTransformer extends PartialTransformerCompanionPlatform { def transform(src: From, failFast: Boolean): partial.Result[To] /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a - * [[io.scalaland.chimney.partial.Result Result]] of transforming `From` to `To`. See an example: + * source of type `A` before transforming it to `To`. See an example: * {{{ * val stringTransformer: PartialTransformer[String, Int] = * PartialTransformer.fromFunction(_.length) * - * case class Length(length: Int) + * case class Id(id: String) * - * implicit val toLengthTransformer: PartialTransformer[String, Length] = - * stringTransformer.map(id => Length(id)) + * implicit val idTransformer: PartialTransformer[Id, Int] = + * stringTransformer.contramap(_.id) * }}} * * @param f - * a pure function that maps a value of `To` to `A` + * a pure function that maps a value of `A` to `From` * @return * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] * * @since 1.5.0 */ - final def map[A](f: To => A): PartialTransformer[From, A] = new PartialTransformer[From, A] { - override def transform(src: From, failFast: Boolean): partial.Result[A] = - self.transform(src, failFast) match { - case partial.Result.Value(to) => partial.Result.Value(f(to)) - case errs: partial.Result.Errors => errs.asInstanceOf[partial.Result[A]] - } + final def contramap[A](f: A => From): PartialTransformer[A, To] = new PartialTransformer[A, To] { + override def transform(src: A, failFast: Boolean): partial.Result[To] = + self.transform(f(src), failFast) } /** Creates a new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] by applying a pure function to a - * source of type `A` before transforming it to `To`. See an example: + * [[io.scalaland.chimney.partial.Result Result]] of transforming `From` to `To`. See an example: * {{{ * val stringTransformer: PartialTransformer[String, Int] = * PartialTransformer.fromFunction(_.length) * - * case class Id(id: String) + * case class Length(length: Int) * - * implicit val idTransformer: PartialTransformer[Id, Int] = - * stringTransformer.contramap(_.id) + * implicit val toLengthTransformer: PartialTransformer[String, Length] = + * stringTransformer.map(id => Length(id)) * }}} * * @param f - * a pure function that maps a value of `A` to `From` + * a pure function that maps a value of `To` to `A` * @return * new [[io.scalaland.chimney.PartialTransformer PartialTransformer]] * * @since 1.5.0 */ - final def contramap[A](f: A => From): PartialTransformer[A, To] = new PartialTransformer[A, To] { - override def transform(src: A, failFast: Boolean): partial.Result[To] = - self.transform(f(src), failFast) + final def map[A](f: To => A): PartialTransformer[From, A] = new PartialTransformer[From, A] { + override def transform(src: From, failFast: Boolean): partial.Result[A] = + self.transform(src, failFast) match { + case partial.Result.Value(to) => partial.Result.Value(f(to)) + case errs: partial.Result.Errors => errs.asInstanceOf[partial.Result[A]] + } } } } diff --git a/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala b/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala index e4f6186a7..b7fa486a3 100644 --- a/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala +++ b/chimney/src/test/scala/io/scalaland/chimney/PartialTransformerSpec.scala @@ -114,4 +114,34 @@ class PartialTransformerSpec extends ChimneySpec { id.intoPartial[Length].transform ==> partial.Result.fromValue(Length(id.toInt)) id.intoPartial[Prefix].transform ==> partial.Result.fromValue(Prefix.BarPrefix) } + + test("contramap") { + case class Id(id: String) + + trait Prefix { + def value: String + } + + object Prefix { + case object FooPrefix extends Prefix { + override def value: String = "1" + } + + case object BarPrefix extends Prefix { + override def value: String = "2" + } + } + + implicit val idTransformer: PartialTransformer[Id, Int] = + pt1.contramap(_.id) + + implicit val prefixTransformer: PartialTransformer[Prefix, Int] = + pt1.contramap(_.value) + + val id = "1" + Id(id).intoPartial[Int].transform ==> partial.Result.fromValue(id.toInt) + + val prefix: Prefix = Prefix.FooPrefix + prefix.intoPartial[Int].transform ==> partial.Result.fromValue(id.toInt) + } }