Skip to content

Commit 1e2a963

Browse files
Test and document result.orElse, update Cats typeclasses docs, rename category implicits to arrow implicits
1 parent 8cfb9c0 commit 1e2a963

File tree

7 files changed

+63
-8
lines changed

7 files changed

+63
-8
lines changed

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsPartialTransformerImplicits.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import io.scalaland.chimney.PartialTransformer
99
trait CatsPartialTransformerImplicits {
1010

1111
/** @since 1.0.0 */
12-
implicit final val catsCategoryForPartialTransformer
12+
implicit final val catsArrowForPartialTransformer
1313
: ArrowChoice[PartialTransformer] & CommutativeArrow[PartialTransformer] =
1414
new ArrowChoice[PartialTransformer] with CommutativeArrow[PartialTransformer] {
1515
override def lift[A, B](f: A => B): PartialTransformer[A, B] = PartialTransformer.fromFunction(f)

chimney-cats/src/main/scala/io/scalaland/chimney/cats/CatsTotalTransformerImplicits.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import io.scalaland.chimney.Transformer
77
trait CatsTotalTransformerImplicits {
88

99
/** @since 1.0.0 */
10-
implicit final val catsCategoryForTransformer: ArrowChoice[Transformer] & CommutativeArrow[Transformer] =
10+
implicit final val catsArrowForTransformer: ArrowChoice[Transformer] & CommutativeArrow[Transformer] =
1111
new ArrowChoice[Transformer] with CommutativeArrow[Transformer] {
1212
override def lift[A, B](f: A => B): Transformer[A, B] = f(_)
1313

chimney-cats/src/test/scala/io/scalaland/chimney/cats/PartialResultLaws.scala

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class PartialResultLaws extends ChimneySpec with utils.ArbitraryUtils {
2929
UnorderedTraverseTests[partial.Result].unorderedTraverse[Int, Long, Double, Const[Unit, *], Const[Int, *]]
3030
)
3131
checkLawsAsTests(TraverseTests[partial.Result].traverse[Int, Long, Double, Byte, Const[Unit, *], Const[Int, *]])
32+
checkLawsAsTests(SemigroupKTests[partial.Result].semigroupK[Int])
33+
checkLawsAsTests(MonoidKTests[partial.Result].monoidK[Int])
3234
checkLawsAsTests(AlternativeTests[partial.Result].alternative[Int, String, Double])
3335
}
3436
}

chimney-cats/src/test/scala/io/scalaland/chimney/cats/PartialTransformerLaws.scala

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class PartialTransformerLaws extends ChimneySpec with utils.ArbitraryUtils {
3232
MonadErrorTests[PartialTransformer[String, *], partial.Result.Errors].monadError[Int, String, Double]
3333
)
3434
checkLawsAsTests(CoflatMapTests[PartialTransformer[String, *]].coflatMap[Int, String, Double])
35+
checkLawsAsTests(SemigroupKTests[PartialTransformer[String, *]].semigroupK[Int])
36+
checkLawsAsTests(MonoidKTests[PartialTransformer[String, *]].monoidK[Int])
3537
checkLawsAsTests(AlternativeTests[PartialTransformer[String, *]].alternative[Int, String, Double])
3638
}
3739

chimney/src/main/scala/io/scalaland/chimney/partial/Result.scala

+18
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,24 @@ sealed trait Result[+A] {
140140
case _: Result.Errors => this.asInstanceOf[Result[B]]
141141
}
142142

143+
/** Prepends a [[io.scalaland.chimney.partial.PathElement]] to all errors represented by this result.
144+
*
145+
* @tparam B the element type of the returned result
146+
* @param result lazy [[io.scalaland.chimney.partial.Result]] to compute as a fallback if this one has errors
147+
* @return a [[io.scalaland.chimney.partial.Result]] with the first successful value or a failure combining errors
148+
* from both results
149+
*
150+
* @since 1.0.0
151+
*/
152+
final def orElse[B >: A](result: => Result[B]): Result[B] = this match {
153+
case _: Result.Value[?] => this
154+
case e: Result.Errors =>
155+
result match {
156+
case r: Result.Value[?] => r
157+
case e2: Result.Errors => Result.Errors.merge(e, e2)
158+
}
159+
}
160+
143161
/** Prepends a [[io.scalaland.chimney.partial.PathElement]] to all errors represented by this result.
144162
*
145163
* @param pathElement [[io.scalaland.chimney.partial.PathElement]] to be prepended

chimney/src/test/scala/io/scalaland/chimney/PartialResultSpec.scala

+32
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,38 @@ class PartialResultSpec extends ChimneySpec {
135135
result2.asErrorPathMessageStrings ==> Iterable("" -> """For input string: "error2"""")
136136
}
137137

138+
test("orElse fallbacks on another Result on error, aggregating errors if both Results fail") {
139+
var used = false
140+
val result = partial.Result.fromValue(1).orElse {
141+
used = true
142+
partial.Result.fromValue(2)
143+
}
144+
result.asOption ==> Some(1)
145+
result.asEither ==> Right(1)
146+
result.asErrorPathMessageStrings ==> Iterable()
147+
used ==> false
148+
149+
var used2 = false
150+
val result2 = partial.Result.fromEmpty[Int].orElse {
151+
used2 = true
152+
partial.Result.fromValue(2)
153+
}
154+
result2.asOption ==> Some(2)
155+
result2.asEither ==> Right(2)
156+
result2.asErrorPathMessageStrings ==> Iterable()
157+
used2 ==> true
158+
159+
var used3 = false
160+
val result3 = partial.Result.fromEmpty[Int].orElse {
161+
used3 = true
162+
partial.Result.fromEmpty[Int]
163+
}
164+
result3.asOption ==> None
165+
result3.asEither.isLeft ==> true
166+
result3.asErrorPathMessageStrings ==> Iterable("" -> """empty value""", "" -> """empty value""")
167+
used3 ==> true
168+
}
169+
138170
test("fromFunction converts function into function returning Result") {
139171
partial.Result.fromFunction[Int, Int](_ * 2).apply(3) ==> partial.Result.fromValue(6)
140172
}

docs/docs/cookbook.md

+7-6
Original file line numberDiff line numberDiff line change
@@ -295,20 +295,21 @@ Cats integration module contains the following utilities:
295295
- for `Transformer` type class:
296296
- `ArrowChoice[Transformer] & CommutativeArrow[Transformer]` (implementing also `Arrow`, `Choice`, `Category`,
297297
`Compose`, `Strong`, `Profunctor`)
298-
- `[Source] => Monad[Transformer[Source, *]] with CoflatMap[Transformer[Source, *]]` (implementing also `Monad`,
299-
`Applicative`, `Functor`)
298+
- `[Source] => Monad[Transformer[Source, *]] & CoflatMap[Transformer[Source, *]]`
299+
(implementing also `Monad`, `Applicative`, `Functor`)
300300
- `[Target] => Contravariant[Transformer[*, Target]]` (implementing also `Invariant`)
301301
- for `PartialTransformer` type class:
302302
- `ArrowChoice[PartialTransformer] & CommutativeArrow[PartialTransformer]` (implementing also `Arrow`, `Choice`,
303303
`Category`,`Compose`, `Strong`, `Profunctor`)
304-
- `[Source] => MonadError[PartialTransformer[Source, *], partial.Result.Errors] with CoflatMap[PartialTransformer[Source, *]]`
305-
(implementing also `Monad`, `Applicative`, `Functor`, `ApplicativeError`)
304+
- `[Source] => MonadError[PartialTransformer[Source, *], partial.Result.Errors] & CoflatMap[PartialTransformer[Source, *]] & Alternative[PartialTransformer[Source, *]]`
305+
(implementing also `Monad`, `Applicative`, `Functor`, `ApplicativeError`, `NonEmptyAlternative`, `MonoidK`,
306+
`SemigroupK`)
306307
- `[Source] => Parallel[PartialTransformer[Source, *]]` (implementing also `NonEmptyParallel`)
307308
- `[Target] => Contravariant[Transformer[*, Target]]` (implementing also `Invariant`)
308309
- for `partial.Result` data type:
309-
- `MonadError[partial.Result, partial.Result.Errors] & CoflatMap[partial.Result] & Traverse[partial.Result]`
310+
- `MonadError[partial.Result, partial.Result.Errors] & CoflatMap[partial.Result] & Traverse[partial.Result] $ Alternative[partial.Result]`
310311
(implementing also `Monad`, `Applicative`, `Functor`, `ApplicativeError`, `UnorderedTraverse`, `Foldable`,
311-
`UnorderedFoldable`, `Invariant[partial.Result]`, `Semigriupal[partial.Result]`, ...)
312+
`UnorderedFoldable`, `Invariant`, `Semigriupal`, `NonEmptyAlternative`, `SemigroupK`, `MonoidK`)
312313
- `Parallel[partial.Result]` (implementing also`NonEmptyParallel`)
313314
- `Semigroup[partial.Result.Errors]`
314315

0 commit comments

Comments
 (0)