2017-02-06 2 views
2

Aujourd'hui, je suis tombé sur le problème suivant: J'ai eu quelques pattern matching en cours, simplifié qui ressemble à ceci:modèle Scala correspondant à async (ou tout monade) gardes

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

Maintenant, je me suis dit, malheureusement, sur , que mes gardes/extracteurs seront Future[Boolean]. Imaginez qu'ils appellent un service Web externe pour obtenir le résultat. Évidemment, je ne peux pas utiliser le modèle de garde ou d'extraction avec un avenir (ou n'importe quelle monade). Donc, maintenant je veux vérifier chaque condition de manière asynchrone, mais casser la première réussie. Fondamentalement, je veux le contraire du flux monadique normal - ce qui signifie l'arrêt sur le premier succès.
Ma mise en œuvre semble bien fonctionner, mais je suis curieux de voir s'il y a un moyen plus simple ou quel type de modèle que vous utiliseriez dans ce cas. Rappelez-vous que mon exemple est extrêmement simple pour être un exemple.

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 
} 

(BTW. Il est scala 2.12)
Je serais heureux suggestions :)

+0

Je pense que vous avez factorisé trop loin. Essayez 'def asyncPatternMatch (i: Futur [Int]): Match [MatchType] = i.map (syncPatternMatch)'. –

+0

Mh mes gardes sont asynchrones, imaginez 'isBigEnough' appelle une API externe pour obtenir le résultat. Je ne vois pas vraiment comment une entrée 'Future [Int]' en tant qu'entrée m'aiderait ici. – rincewind

Répondre

1

Qu'est-ce que vous avez besoin est un 'monade si' comme the cats one. Nous pouvons effectivement rouler une version simplifiée spécifiquement pour 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

cela fait l'affaire, merci, ne savait pas de 'ifM' – rincewind

+0

@rincewind vous êtes les bienvenus. 'ifM' n'est qu'un combinateur monadique, ce qui me rappelle que j'aurais pu l'implémenter beaucoup plus simplement. Mise à jour de ma réponse – Yawar