与异步(或任何 monad)守卫匹配的 Scala 模式
Scala pattern matching with async (or any monad) guards
今天遇到了以下问题:
我进行了一些模式匹配,简化 看起来像这样:
object Sync {
sealed trait MatchType
case object BigType extends MatchType
case object SmallType extends MatchType
case object EvenType extends MatchType
case object UnknownType extends MatchType
def syncPatternMatch(i: Int): MatchType = i match {
case _ if i > 100 => BigType
case _ if i < 3 => SmallType
case _ if i % 2 == 0 => EvenType
case _ => UnknownType
}
}
现在不幸的是我发现,我的 guards/extractors 将是 Future[Boolean]
。想象一下,他们调用外部 Web 服务来获取结果。
显然我不能将守卫或提取器模式与 Future(或任何 monad)一起使用。
所以现在我想异步检查每个条件,但中断第一个成功的条件。
基本上我想要与正常的单子流相反的东西——意思是在第一次成功时停止。
我的实现似乎运行良好,但我很好奇是否有更简单的方法或在这种情况下您会使用哪种模式。
请记住,为了作为示例,我的示例非常简单。
import cats.data.EitherT
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object Async {
sealed trait MatchType
case object BigType extends MatchType
case object SmallType extends MatchType
case object EvenType extends MatchType
case object UnknownType extends MatchType
type Match[B] = EitherT[Future, MatchType, B]
def isBigEnough(i: Int): Match[Unit] = Future(if(i > 100) Left(BigType) else Right(()))
def isVerySmall(i: Int): Match[Unit] = Future(if(i < 3) Left(SmallType) else Right(()))
def isEven(i: Int): Match[Unit] = Future(if(i % 2 == 0) Left(EvenType) else Right(()))
def otherwise: Match[MatchType] = Future.successful(Right(UnknownType))
implicit def liftFutureEither[A, B](f: Future[Either[A, B]]): EitherT[Future, A, B] = EitherT(f)
implicit def extractFutureEither[A, B](e: EitherT[Future, A, B]): Future[Either[A, B]] = e.value
def asyncPatternMatch(i: Int): Match[MatchType] = for {
_ <- isBigEnough(i)
_ <- isVerySmall(i)
_ <- isEven(i)
default <- otherwise
} yield default
asyncPatternMatch(10).foreach(either => println(either.fold(identity, identity)))
// EvenType
}
(顺便说一句,它是 scala 2.12)
我很乐意提出建议:)
你需要的是像the cats one这样的'monadic if'。我们实际上可以推出专门针对 Future
:
的简化版本
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
sealed trait MatchType
object MatchType {
case object Big extends MatchType
case object Small extends MatchType
case object Even extends MatchType
case object Unknown extends MatchType
def isBigEnough(i: Int): Future[Boolean] = Future successful (i > 100)
def isVerySmall(i: Int): Future[Boolean] = Future successful (i < 3)
def isEven(i: Int): Future[Boolean] = Future successful (i % 2 == 0)
def ifFuture[A](
test: Future[Boolean],
trueCase: => A,
falseCase: => Future[A]): Future[A] =
test flatMap { t =>
if (t) Future successful trueCase else falseCase
}
def apply(i: Int): Future[MatchType] =
ifFuture(isBigEnough(i), Big,
ifFuture(isVerySmall(i), Small,
ifFuture(isEven(i), Even,
Future successful Unknown)))
}
今天遇到了以下问题: 我进行了一些模式匹配,简化 看起来像这样:
object Sync {
sealed trait MatchType
case object BigType extends MatchType
case object SmallType extends MatchType
case object EvenType extends MatchType
case object UnknownType extends MatchType
def syncPatternMatch(i: Int): MatchType = i match {
case _ if i > 100 => BigType
case _ if i < 3 => SmallType
case _ if i % 2 == 0 => EvenType
case _ => UnknownType
}
}
现在不幸的是我发现,我的 guards/extractors 将是 Future[Boolean]
。想象一下,他们调用外部 Web 服务来获取结果。
显然我不能将守卫或提取器模式与 Future(或任何 monad)一起使用。
所以现在我想异步检查每个条件,但中断第一个成功的条件。
基本上我想要与正常的单子流相反的东西——意思是在第一次成功时停止。
我的实现似乎运行良好,但我很好奇是否有更简单的方法或在这种情况下您会使用哪种模式。
请记住,为了作为示例,我的示例非常简单。
import cats.data.EitherT
import cats.implicits._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
object Async {
sealed trait MatchType
case object BigType extends MatchType
case object SmallType extends MatchType
case object EvenType extends MatchType
case object UnknownType extends MatchType
type Match[B] = EitherT[Future, MatchType, B]
def isBigEnough(i: Int): Match[Unit] = Future(if(i > 100) Left(BigType) else Right(()))
def isVerySmall(i: Int): Match[Unit] = Future(if(i < 3) Left(SmallType) else Right(()))
def isEven(i: Int): Match[Unit] = Future(if(i % 2 == 0) Left(EvenType) else Right(()))
def otherwise: Match[MatchType] = Future.successful(Right(UnknownType))
implicit def liftFutureEither[A, B](f: Future[Either[A, B]]): EitherT[Future, A, B] = EitherT(f)
implicit def extractFutureEither[A, B](e: EitherT[Future, A, B]): Future[Either[A, B]] = e.value
def asyncPatternMatch(i: Int): Match[MatchType] = for {
_ <- isBigEnough(i)
_ <- isVerySmall(i)
_ <- isEven(i)
default <- otherwise
} yield default
asyncPatternMatch(10).foreach(either => println(either.fold(identity, identity)))
// EvenType
}
(顺便说一句,它是 scala 2.12)
我很乐意提出建议:)
你需要的是像the cats one这样的'monadic if'。我们实际上可以推出专门针对 Future
:
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
sealed trait MatchType
object MatchType {
case object Big extends MatchType
case object Small extends MatchType
case object Even extends MatchType
case object Unknown extends MatchType
def isBigEnough(i: Int): Future[Boolean] = Future successful (i > 100)
def isVerySmall(i: Int): Future[Boolean] = Future successful (i < 3)
def isEven(i: Int): Future[Boolean] = Future successful (i % 2 == 0)
def ifFuture[A](
test: Future[Boolean],
trueCase: => A,
falseCase: => Future[A]): Future[A] =
test flatMap { t =>
if (t) Future successful trueCase else falseCase
}
def apply(i: Int): Future[MatchType] =
ifFuture(isBigEnough(i), Big,
ifFuture(isVerySmall(i), Small,
ifFuture(isEven(i), Even,
Future successful Unknown)))
}