Scala模式与异步(或任何monad)防护匹配

时间:2017-02-06 17:43:27

标签: scala pattern-matching monads future scala-cats

今天我遇到了以下问题: 我进行了一些模式匹配,简化看起来像这样:

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
  }
}

现在不幸的是,我发现,我的警卫/提取器将是Future[Boolean]。想象一下,他们调用外部Web服务来获得结果。 显然我不能使用带有Future(或任何monad)的防护或提取器模式。

所以现在我想要异步检查每个条件,但是在第一个成功的条件下中断。

基本上我想要与正常的monadic流相反 - 意味着停止第一次成功 我的实现似乎运行良好,但我很高兴看到在这种情况下是否有更简单的方法或使用什么样的模式。

请记住,为了成为一个例子,我的例子非常简单。

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)
我很乐意提出建议:)

1 个答案:

答案 0 :(得分:1)

你需要的是一个monadic,如果&#39;比如the cats one。我们实际上可以专门为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)))
}