Skip to content

Commit

Permalink
Expose Left/Right cast syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
adelbertc committed Aug 17, 2016
1 parent eedd057 commit e38ec8d
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 6 deletions.
3 changes: 1 addition & 2 deletions core/src/main/scala/cats/data/EitherT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package data

import cats.functor.Bifunctor
import cats.instances.either._
import cats.syntax.EitherUtil
import cats.syntax.either._

/**
Expand Down Expand Up @@ -73,7 +72,7 @@ final case class EitherT[F[_], A, B](value: F[Either[A, B]]) {

def flatMap[D](f: B => EitherT[F, A, D])(implicit F: Monad[F]): EitherT[F, A, D] =
EitherT(F.flatMap(value) {
case l @ Left(_) => F.pure(EitherUtil.leftCast(l))
case l @ Left(_) => F.pure(l.rightCast)
case Right(b) => f(b).value
})

Expand Down
21 changes: 17 additions & 4 deletions core/src/main/scala/cats/syntax/either.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ trait EitherSyntax {
implicit def catsSyntaxEither[A, B](eab: Either[A, B]): EitherOps[A, B] = new EitherOps(eab)

implicit def catsSyntaxEitherObject(either: Either.type): EitherObjectOps = new EitherObjectOps(either) // scalastyle:off ensure.single.space.after.token

implicit def catsSyntaxLeft[A, B](left: Left[A, B]): LeftOps[A, B] = new LeftOps(left)

implicit def catsSyntaxRight[A, B](right: Right[A, B]): RightOps[A, B] = new RightOps(right)
}

final class EitherOps[A, B](val eab: Either[A, B]) extends AnyVal {
Expand Down Expand Up @@ -311,10 +315,19 @@ final class CatchOnlyPartiallyApplied[T] private[syntax] {
}
}

final class LeftOps[A, B](val left: Left[A, B]) extends AnyVal {
/** Cast the right type parameter of the `Left`. */
def rightCast[C]: Either[A, C] = left.asInstanceOf[Either[A, C]]
}

final class RightOps[A, B](val right: Right[A, B]) extends AnyVal {
/** Cast the left type parameter of the `Right`. */
def leftCast[C]: Either[C, B] = right.asInstanceOf[Either[C, B]]
}

/** Convenience methods to use `Either` syntax inside `Either` syntax definitions. */
private[cats] object EitherUtil {
/** Cast the *right* type parameter of a `Left`. */
def leftCast[A, B, C](l: Left[A, B]): Either[A, C] = l.asInstanceOf[Left[A, C]]
def leftCast[A, B, C](l: Left[A, B]): Either[A, C] = new LeftOps(l).rightCast[C]

/** Cast the *left* type parameter of a `Right` */
def rightCast[A, B, C](r: Right[A, B]): Either[C, B] = r.asInstanceOf[Right[C, B]]
def rightCast[A, B, C](r: Right[A, B]): Either[C, B] = new RightOps(r).leftCast[C]
}
8 changes: 8 additions & 0 deletions tests/src/test/scala/cats/tests/EitherTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ class EitherTests extends CatsSuite {
checkAll("Either[Int, String]", orderLaws.partialOrder(partialOrder))
checkAll("Either[Int, String]", orderLaws.order(order))

test("Left/Right syntax") {
forAll { (e: Either[Int, String]) =>
e match {
case l @ Left(_) => l.rightCast[Double].isLeft should === (true)
case r @ Right(_) => r.leftCast[Int].isRight should === (true)
}
}
}

test("implicit instances resolve specifically") {
val eq = catsStdEqForEither[Int, String]
Expand Down

0 comments on commit e38ec8d

Please sign in to comment.