对于理解Scala中的以下代码段,什么是最好的(无需线性/回调、较少样板)形式?
val result = emailTakenFuture.flatMap { emailTaken =>
if (emailTaken) {
Future.successful(SignUpResult.EmailAlreadyTaken)
} else {
usernameTakenFuture.flatMap { usernameTaken =>
if (usernameTaken) {
Future.successful(SignUpResult.UsernameAlreadyTaken)
} else {
nextIdFuture.flatMap { userId =>
storeUserFuture(userId).map(user => SignUpResult.Success(user))
}
}
}
}
}
考虑< code >或者重构
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
这是一个工作示例
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
/false
代替了true
/仍旧
。
您可能想考虑将中间结果包装在Throwables中。然后,您可以在以后仅针对这些异常恢复未来模式匹配。
我加入了“样板”以使示例可编译:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext
implicit val executionContext: ExecutionContext = ExecutionContext.global
case class User()
def emailTakenFuture: Future[Boolean] = ???
def usernameTakenFuture: Future[Boolean] = ???
def nextIdFuture: Future[String] = ???
def storeUserFuture(userId: String): Future[User]
为了简洁起见,我扩展了Throwable。您可能希望将注册结果包装在自定义异常中,以便不将它们与 SignupResult
类型一起公开。
trait SignUpResult
case object SignUpResult {
case object EmailAlreadyTaken extends Throwable with SignUpResult
case object UsernameAlreadyTaken extends Throwable with SignUpResult
case class Success(user: User) extends SignUpResult
}
val result: Future[SignUpResult] = {
(for {
emailTaken <- emailTakenFuture
_ <- if (emailTaken) Future.failed(SignUpResult.EmailAlreadyTaken) else Future.successful(Unit)
userNameTaken <- usernameTakenFuture
_ <- if (userNameTaken) Future.failed(SignUpResult.UsernameAlreadyTaken) else Future.successful(Unit)
userId <- nextIdFuture
user <- storeUserFuture(userId)
} yield SignUpResult.Success(user)).recoverWith {
case (SignUpResult.EmailAlreadyTaken) => Future.successful(SignUpResult.EmailAlreadyTaken)
case (SignUpResult.UsernameAlreadyTaken) => Future.successful(SignUpResult.UsernameAlreadyTaken)
}
}
只有最后一个其他
之后的部分才真正适合理解:
for {
userId <- nextIdFuture
user <- storeUserFuture(userId)
} yield SignUpResult.Success(user)
我只是为其余部分编写一个帮助器函数:
def condFlatMap[T](future: Future[Boolean], ifTrue: T)(ifFalse: => Future[T]): Future[T] =
future.flatMap(x => if (x) Future.successful(ifTrue) else ifFalse)
val result =
condFlatMap(emailTakenFuture, SignUpResult.EmailAlreadyTaken) {
condFlatMap(usernameTakenFuture, SignUpResult.UsernameAlreadyTaken) {
for {
userId <- nextIdFuture
user <- storeUserFuture(userId)
} yield SignUpResult.Success(user)
}
}
(未测试,但应大致正确)
如何将此地图/平面图转换为便于理解的图形,请解释其工作原理,谢谢。
这是我的代码 上面的代码给出了 如果我将用于理解的屈服值更改为则结果为 如果我将更改为
我有这样一个代码: 你能帮我完成代码库吗?
我有一个java map:
我有一个带有对象的数组()。对象包含一个 orgId 和一个 orgName。 有没有一种简便的方法可以将它转换成< code>Map[Long,String] (orgId,orgName)
给定和: 如何将此转换为? 请注意,我提供了上述内容,作为在Scala中使用for constancement、Try和sequences的答案,但在将其转换为