Skip to content

Commit

Permalink
Add Either#toEitherNel extension and friends (#2475)
Browse files Browse the repository at this point in the history
* Add Either#toEitherNel extension

* Add EitherObject.{leftNel, rightNel}, Ior#toIorNel
  • Loading branch information
kubukoz authored and Luka Jacobowitz committed Sep 30, 2018
1 parent 0a377a3 commit 15f4e59
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 1 deletion.
1 change: 1 addition & 0 deletions core/src/main/scala/cats/data/Ior.scala
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ sealed abstract class Ior[+A, +B] extends Product with Serializable {
final def pad: (Option[A], Option[B]) = fold(a => (Some(a), None), b => (None, Some(b)), (a, b) => (Some(a), Some(b)))
final def unwrap: Either[Either[A, B], (A, B)] = fold(a => Left(Left(a)), b => Left(Right(b)), (a, b) => Right((a, b)))

final def toIorNel[AA >: A]: IorNel[AA, B] = leftMap(NonEmptyList.one)
final def toEither: Either[A, B] = fold(Left(_), Right(_), (_, b) => Right(b))
final def toValidated: Validated[A, B] = fold(Invalid(_), Valid(_), (_, b) => Valid(b))
final def toOption: Option[B] = right
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/scala/cats/syntax/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,8 @@ final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal {
*/
def toEitherT[F[_]: Applicative]: EitherT[F, A, B] = EitherT.fromEither(eab)

def toEitherNel[AA >: A]: EitherNel[AA, B] = leftMap(NonEmptyList.one)

def raiseOrPure[F[_]](implicit ev: ApplicativeError[F, A]): F[B] =
ev.fromEither(eab)

Expand All @@ -290,6 +292,10 @@ final class EitherObjectOps(val either: Either.type) extends AnyVal { // scalast

def right[A, B](b: B): Either[A, B] = Right(b)

def leftNel[A, B](a: A): EitherNel[A, B] = Left(NonEmptyList.one(a))

def rightNel[A, B](b: B): EitherNel[A, B] = Right(b)

/**
* Evaluates the specified block, catching exceptions of the specified type and returning them on the left side of
* the resulting `Either`. Uncaught exceptions are propagated.
Expand Down
24 changes: 23 additions & 1 deletion tests/src/test/scala/cats/tests/EitherSuite.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package cats
package tests

import cats.data.{ EitherT, Validated }
import cats.data.{ EitherT, Validated, NonEmptyList }
import cats.laws.discipline._
import cats.kernel.laws.discipline.{MonoidTests, SemigroupTests, OrderTests, PartialOrderTests, EqTests}
import scala.util.Try
Expand Down Expand Up @@ -109,6 +109,18 @@ class EitherSuite extends CatsSuite {
}
}

test("leftNel is consistent with left(NEL)") {
forAll { s: String =>
Either.leftNel[String, Int](s) should === (Either.left[NonEmptyList[String], Int](NonEmptyList.one(s)))
}
}

test("rightNel is consistent with right") {
forAll { i: Int =>
Either.rightNel[String, Int](i) should === (Either.right[NonEmptyList[String], Int](i))
}
}

test("double swap is identity") {
forAll { (x: Either[Int, String]) =>
x.swap.swap should === (x)
Expand Down Expand Up @@ -268,6 +280,16 @@ class EitherSuite extends CatsSuite {
either.show should === ("Left(string)")
}

test("toEitherNel Left") {
val either = Either.left[String, Int]("oops")
either.toEitherNel should === (Either.left[NonEmptyList[String], Int](NonEmptyList.one("oops")))
}

test("toEitherNel Right") {
val either = Either.right[String, Int](42)
either.toEitherNel should === (Either.right[NonEmptyList[String], Int](42))
}

test("ap consistent with Applicative") {
val fab = implicitly[Applicative[Either[String, ?]]]
forAll { (fa: Either[String, Int],
Expand Down
15 changes: 15 additions & 0 deletions tests/src/test/scala/cats/tests/IorSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,21 @@ class IorSuite extends CatsSuite {
}
}

test("toIorNel Left") {
val ior = Ior.left[String, Int]("oops")
ior.toIorNel should === (Ior.left[NonEmptyList[String], Int](NonEmptyList.one("oops")))
}

test("toIorNel Right") {
val ior = Ior.right[String, Int](42)
ior.toIorNel should === (Ior.right[NonEmptyList[String], Int](42))
}

test("toIorNel Both") {
val ior = Ior.both[String, Int]("oops", 42)
ior.toIorNel should === (Ior.both[NonEmptyList[String], Int](NonEmptyList.one("oops"), 42))
}

test("leftNel") {
forAll { (x: String) =>
Ior.leftNel(x).left should === (Some(NonEmptyList.one(x)))
Expand Down

0 comments on commit 15f4e59

Please sign in to comment.