2017-02-06 53 views
2

今天我碰到下面的問題就來了: 我有一些模式匹配回事,簡化看起來像這樣:斯卡拉模式與異步匹配(或任何單子)守衛

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服務來獲得結果。 顯然,我不能使用未來(或任何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 
} 

(順便說一句,這是斯卡拉2.12)
我會很高興的建議:)

+0

我想你已經考慮得太過分了。試試'def asyncPatternMatch(i:Future [Int]):Match [MatchType] = i.map(syncPatternMatch)'。 –

+0

我的衛兵是異步的,想象一下'isBigEnough'正在調用外部API來獲得結果。 不要真的看到'Future [Int]'作爲輸入如何幫助我。 – rincewind

回答

1

你需要的是一個 '一元,如果' 像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))) 
} 
+1

這是伎倆,謝謝,不知道'ifM' – rincewind

+0

@ rincewind不用客氣。 'ifM'只是一個monadic組合器,這讓我想起我可以更簡單地實現它。更新我的答案。 – Yawar