= for { userOpt <- userRepository.findById(userId) result <- userOpt match { case Some(user) => user.rename(newName) match { case Right(updated) => userRepository.store(updated) case Left(error) => Future.successful(Result.Error(error)) } case None => Future.successful(Result.UserNotFound) } _ <- result match { case Result.Success => notificationService.run(...) case _ => Future.unit } } yield result
= for { userOpt <- userRepository.findById(userId) result <- userOpt match { case Some(user) => user.rename(newName) match { // ユーザーが存在し、リネームに成功した場合のみ永続化 case Right(updated) => userRepository.store(updated) case Left(error) => Future.successful(Result.Error(error)) } case None => Future.successful(Result.UserNotFound) } _ <- result match { case Result.Success => notificationService.run(...) case _ => Future.unit } } yield result
= for { userOpt <- userRepository.findById(userId) result <- userOpt match { case Some(user) => user.rename(newName) match { // ユーザーが存在し、リネームに成功した場合のみ永続化 case Right(updated) => userRepository.store(updated) case Left(error) => Future.successful(Result.Error(error)) } case None => // ユーザーが見つからないなら NotFound Future.successful(Result.UserNotFound) } _ <- result match { case Result.Success => notificationService.run(...) case _ => Future.unit } } yield result
= for { userOpt <- userRepository.findById(userId) result <- userOpt match { case Some(user) => user.rename(newName) match { // ユーザーが存在し、リネームに成功した場合のみ永続化 case Right(updated) => userRepository.store(updated) case Left(error) => Future.successful(Result.Error(error)) } case None => // ユーザーが見つからないなら NotFound Future.successful(Result.UserNotFound) } _ <- result match { case Result.Success => notificationService.run(...) case _ => Future.unit // 成功していないなら何もしない } } yield result
= for { userOpt <- userRepository.findById(userId) result <- userOpt match { case Some(user) => user.rename(newName) match { // ユーザーが存在し、リネームに成功した場合のみ永続化 case Right(updated) => userRepository.store(updated) case Left(error) => Future.successful(Result.Error(error)) } case None => // ユーザーが見つからないなら NotFound Future.successful(Result.UserNotFound) } _ <- result match { case Result.Success => notificationService.run(...) case _ => Future.unit // 成功していないなら何もしない } } yield result for 式を ”フラット ”に 書けない
= for { userOpt <- userRepository.findById(userId) result <- userOpt match { case Some(user) => user.rename(newName) match { // ユーザーが存在し、リネームに成功した場合のみ永続化 case Right(updated) => userRepository.store(updated) case Left(error) => Future.successful(Result.Error(error)) } case None => // ユーザーが見つからないなら NotFound Future.successful(Result.UserNotFound) } _ <- result match { case Result.Success => notificationService.run(...) case _ => Future.unit // 成功していないなら何もしない } } yield result Explicit な 分岐に 都度対処
Future[Result] = for { userE <- userRepository.findById(userId) resultE <- userE match { case Right(user) => for { updatedE <- user.rename(newName) result <- updatedE match { case Right(updated) => userRepository.store(updated).map(_ => Result.Success) case Left(error) => Future.successful(Left(Result.OpsError(error))) } } yield result case Left(_) => Future.successful(Left(Result.UserNotFound)) } _ <- resultE match { case Right(Result.Success) => notificationService.run(...) case Left(Result.OpsError(_)) => Future.successful(()) case Left(Result.UserNotFound) => Future.successful } } yield result
Future[Result] = for { userE <- userRepository.findById(userId) resultE <- userE match { case Right(user) => for { updatedE <- user.rename(newName) result <- updatedE match { case Right(updated) => userRepository.store(updated).map(_ => Result.Success) case Left(error) => Future.successful(Left(Result.OpsError(error))) } } yield result case Left(_) => Future.successful(Left(Result.UserNotFound)) } _ <- resultE match { case Right(Result.Success) => notificationService.run(...) case Left(Result.OpsError(_)) => Future.successful(()) case Left(Result.UserNotFound) => Future.successful } } yield result
B => EitherT[F, AA, D])(implicit F: Monad[F]): EitherT[F, AA, D] = EitherT(F.flatMap(value) { case l @ Left(_) => F.pure(EitherUtil.rightCast(l)) case Right(b) => f(b).value }) // F を Future に固定するなら def flatMap[AA >: A, D](f: B => EitherT[Future, AA, D]): EitherT[Future, AA, D] = EitherT(value.flatMap { case l @ Left(_) => Future.successful(EitherUtil.rightCast(l)) case Right(b) => f(b).value }) Left になったら f を評価せず 常に Left の値を返し続ける ≒ 準正常系でも処理が短絡