Don't Use Return in Scala?

Early success seems uncommon to me, but anyway here’s my take at simulating early returns in for comprehensions:

object EarlyReturn {
  val db = ""
  def handleTransfer(req: TransferRequest): Either[Err, Result] = {
    val resultNested = for {
      u1 <- req.user1.lookup(db).left.map(Left(_))
      u2 <- req.user2.lookup(db).left.map { e =>
        Right(Result(s"Cannot find ${req.user2}: $e"))
      }
      depositor <- u2.checkAccess(u1).left.map { _ =>
        Right(Result(s"$u2 has not given you deposit access"))
      }
      withdrawal <- u1.withdraw(req.amount).left.map(Left(_))
      conf <- depositor.accept(withdrawal).left.map(Left(_))
    } yield {
      Result(s"You transferred $withdrawal to $u2, confirmation number $conf")
    }
    resultNested.joinLeft
  }
}

case class Err(msg: String)
case class Result(msg: String)
class User() {
  def checkAccess(that: User): Either[Err, User] = ???
  def withdraw(amount: Double): Either[Err, Double] = ???
  def accept(amount: Double): Either[Err, Int] = ???
}
class UserName() {
  def lookup(db: Any): Either[Err, User] = ???
}
case class TransferRequest(user1: UserName, user2: UserName, amount: Double)

Should be correct. I’ll check if I can factor out the .left.map(e => Left(???)) and .left.map(e => Right(???)).

Update: I managed to do so. Here’s the result:

object EarlyReturn {
  val db = ""
  def handleTransfer(req: TransferRequest): Either[Err, Result] = {
    val resultNested = for {
      u1 <- req.user1.lookup(db).nestError
      u2 <- req.user2.lookup(db).earlyResult { e =>
        Result(s"Cannot find ${req.user2}: $e")
      }
      depositor <- u2.checkAccess(u1).earlyResult { _ =>
        Result(s"$u2 has not given you deposit access")
      }
      withdrawal <- u1.withdraw(req.amount).nestError
      conf <- depositor.accept(withdrawal).nestError
    } yield {
      Result(s"You transferred $withdrawal to $u2, confirmation number $conf")
    }
    resultNested.joinLeft
  }

  implicit class LeftNestingEither[A, B](either: Either[A, B]) {
    def nestError: Either[Either[A, Nothing], B] =
      either.left.map(Left(_))

    def earlyResult[C](errorToResult: A => C): Either[Either[Nothing, C], B] =
      either.left.map(a => Right(errorToResult(a)))
  }
}

case class Err(msg: String)
case class Result(msg: String)
class User() {
  def checkAccess(that: User): Either[Err, User] = ???
  def withdraw(amount: Double): Either[Err, Double] = ???
  def accept(amount: Double): Either[Err, Int] = ???
}
class UserName() {
  def lookup(db: Any): Either[Err, User] = ???
}
case class TransferRequest(user1: UserName, user2: UserName, amount: Double)