考虑EitherT重构
type SignupResult[A] = EitherT[Future, SignupError, A]
其中SignupError 是以下 ADT:
sealed trait SignupError
case object EmailAlreadyTaken extends SignupError
case object UsernameAlreadyTaken extends SignupError
case object UserIdError extends SignupError
case object UserCreationError extends SignupError
然后给出以下方法签名
def validateEmail(email: String): SignupResult[Unit] = ???
def validateUsername(username: String): SignupResult[Unit] = ???
def nextId(): SignupResult[String] = ???
def storeUser(userId: String): SignupResult[User] = ???
流变平以清晰易懂
(for {
_ <- validateEmail("picard@starfleet.org")
_ <- validateUsername("picard")
userId <- nextId()
user <- storeUser(userId)
} yield user).value
这是一个有效的example
import cats.data.EitherT
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object EitherTExample extends App {
sealed trait SignupError
case object EmailAlreadyTaken extends SignupError
case object UsernameAlreadyTaken extends SignupError
case object UserIdError extends SignupError
case object UserCreationError extends SignupError
final case class User(id: String, username: String)
type SignupResult[A] = EitherT[Future, SignupError, A]
def validateEmail(email: String): SignupResult[Unit] = EitherT.rightT(())
def validateUsername(username: String): SignupResult[Unit] = EitherT.leftT(UsernameAlreadyTaken)
def nextId(): SignupResult[String] = EitherT.rightT("42424242")
def storeUser(userId: String): SignupResult[User] = EitherT.rightT(User("42424242", "picard"))
val result: Future[Either[SignupError, User]] =
(for {
_ <- validateEmail("picard@starfleet.org")
_ <- validateUsername("picard")
userId <- nextId()
user <- storeUser(userId)
} yield user).value
result.map(v => println(v))
}
哪个输出
Left(UsernameAlreadyTaken)
请注意,我们如何使用 Right/Left 来代替 true/false 进行验证。