When using an EitherT[StateWithSomeFixedStateType, T, U], how do you do some state manipulation when a left is returned?

StackOverflow https://stackoverflow.com/questions/17636912

Question

Say you have an EitherT that looks something like this:

type StateListOfString[+T] = State[List[String], T]
type MyEitherT = EitherT[StateListOfString, Int, Boolean]

If you have a for-comprehension that could return a left:

my computation = for {
  a <- thingThatCouldReturnLeft
  b <- otherThingThatCouldReturnLeft
} yield b

How can you follow up with a for-comprehension that manipulates state before itself returning the left?

I think I want something very close to orElse, but orElse doesn't have access to the value of the left:

  def orElse[AA >: A, BB >: B](x: => EitherT[F, AA, BB])(implicit F: Bind[F]): EitherT[F, AA, BB] = {

If it took something like (x: => Int => EitherT[F, AA, BB]) instead of just (x: => EitherT[F, AA, BB]), it would work.

I had tried starting with:

for {
  a <- myComputation.isLeft
  // OK, now I have something sensible, and I can follow up with something like
  // a leftMap

But if I start by calling isLeft, it looks like the computation is run at least twice, once for the isLeft, and again when I call something like leftMap.

What's the right thing to use here?

Was it helpful?

Solution

Looking at the sources of orElse it seems that it can be naturally generalized as

import scala.language.higherKinds

def onLeft[F[+_],A,B](x: => EitherT[F, A, B])
                     (y: A => EitherT[F, A, B])
                     (implicit F: Bind[F]): EitherT[F, A, B] =
{
  val g = x.run
  EitherT(F.bind(g) {
    case -\/(l) => y(l).run
    case \/-(_) => g
  })
}

This is basically the same thing as swapping left/right and then using monadic binding

def onLeft1[F[+_],A,B](x: => EitherT[F, A, B])
                      (y: A => EitherT[F, A, B])
                      (implicit F: Monad[F]): EitherT[F, A, B] =
  x.swap.flatMap((a: A) => y(a).swap).swap

but of course the first variant is more efficient (and also a bit more general in F).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top