1

Zum Beispiel XorT haben wir einige Dienste mit unterschiedlichen „Container“ Future und Option:verschiedene „Behälter“ Combine bei Katzen

//first service with Future 
class FirstService { 
     getData(): XorT[Future, ServiceError, SomeData] 
} 

//second service with Optin 
class SecondService { 
     getData(): XorT[Option, ServiceError, SomeData] 
} 

Wie verbinden wir können sie Verständnis einer für die Verwendung Typenkonflikt zu vermeiden?

val result = for { 
    data1 <- firstService.getData() 
    data2 <- secondService.getData() // type mismatch required XorT[Future, ServiceError, SomeData] 
} yield mergeResult(data1, data2) 

Antwort

1

Einer der möglichen Wege zur Lösung dieses Problems machen gemeinsame Container Monade für verschiedene Arten (Future, Option):

trait Container[+A] { 
    def map[B](f: A => B): Container[B] 
    def flatMap[B](f: A => Container[B]): Container[B] 
} 
// Empty container for value 
class EmptyContainer[+A](value: A) extends Container[A] { 
    override def map[B](f: (A) => B): Container[B] = new EmptyContainer[B](f(value)) 
    override def flatMap[B](f: (A) => Container[B]): Container[B] = f(value) 
} 
// Implement container for Option 
class OptionContainer[+A](option: Option[A]) extends Container[A] { 
    override def map[B](f: (A) => B): Container[B] = new OptionContainer[B](option.map(f)) 
    override def flatMap[B](f: (A) => Container[B]): Container[B] = option match { 
    case Some(value) => f(value) 
    case None => new OptionContainer[B](None) 
    } 
} 
// Implement container for Future 
class FutureContainer[+A](future: Future[A]) extends Container[A] { 
    override def map[B](f: (A) => B): Container[B] = new FutureContainer[B](future.map(f)) 
    // TODO: can be better!!! 
    override def flatMap[B](f: (A) => Container[B]): Container[B] = { 
    val promise = Promise[B]() 
    future.onComplete { 
     case Success(a) => f(a).map(b => promise.success(b)) 
     case Failure(exception) => promise.failure(exception) 
    } 
    new FutureContainer[B](promise.future) 
    } 
} 

Sie können eine eigene Implementierung für andere Typen hinzufügen.

// Monad for Container 
object Container { 
    implicit def monad = new Monad[Container] { 
    def flatMap[A, B](fa: Container[A])(f: (A) => Container[B]): Container[B] = fa.flatMap(f) 
    def pure[A](x: A): Container[A] = new EmptyContainer[A](x) 
    } 
} 

Unser Service hat jetzt Ansicht:

class SomeContainerService { 
    def getSomeDate(): XorT[Container, Error, SomeData] = 
    XorT.right(Option(SomeData()).toContainer) 

    def getRemoteDate(): XorT[Container, Error, SomeData] = 
    XorT.right(Future(SomeData()).toContainer) 
} 

Erweiterungen Methoden sowohl für Futures und Optionen auf:

def toContainer = OptionContainer(option) 
def toContainer = FutureContainer(future) 

Und für Verständnis funktionieren:

val result: XorT[Container, Error, SomeData] = for { 
    data1 <- someContainerService.getRemoteDate() // future 
    data2 <- someContainerService.getSomeDate() // option 
} yield { 
    mergeResult(data1, data2) 
} 
2

XorT[F, A, B] ist nur eine bequeme Wrapper über F[A Xor B], so dass Sie die Frage im Wesentlichen sind: wie ein Future zu kombinieren und ein Option. Da Sie immer noch eine Future in irgendeiner Form zurückgeben müssen, wird dies hauptsächlich: wie mit der Option umzugehen.

gibt es mehrere Möglichkeiten:

import scala.concurrent.Future 
import scala.concurrent.ExecutionContext.Implicits.global 
import cats.data.XorT 
import cats.implicits._ 

type ServiceError = String 
type FutureErrorOr[A] = XorT[Future, ServiceError, A] 

val fXorT: FutureErrorOr[Int] = XorT.right(Future.successful(1)) 
val oXorT: XorT[Option, ServiceError, Int] = XorT.right(1.some) 
  • die Option in eine Future Drehen (None-Future.failed):

    val opt2fut: FutureErrorOr[Int] = 
        XorT(oXorT.value.fold(
        Future.failed[ServiceError Xor Int](new NoSuchElementException())(
        Future.successful _)) 
    
    for { a <- fXort; b <- opt2fut } yield a + b 
    
  • Drehen Sie den Option in eine ServiceError Xor ? (None-Xor.Left) :

    val opt2xor: FutureErrorOr[Int] = 
        XorT.fromXor[Future](oXorT.value.getOrElse("no elem".left)) 
    
    for { a <- fXort; b <- opt2xor } yield a + b 
    
  • Ändern Sie Ihre Rückkehr Typ XorT[Future, ServiceError, Option[X]] (dies kann nicht sinnvoll sein, wenn Sie die X im übrigen die für das Verständnis verwenden müssen):

    val optInside: FutureErrorOr[Option[Int]] = 
        XorT.fromXor[Future](oXorT.value.sequenceU) 
    
    for { a <- fXorT; b <- optInside } yield b.map(_ + a) 
    
+0

vielleicht besser um einen universellen Behälter zu machen, der es kann stellen Sie als Zukunft oder Wahl oder irgendjemand anderes, etwas wie XorT dar [UniverseContainer, SeviceError, Wert] – NiceTheo

+1

Ich bin nicht sicher, wie Sie solch einen 'UniverseContainer' schaffen würden? –

+0

Ich denke auch darüber nach, wie einige Wrapper mit eigenen flatMap - UniversalContainer (Option), UniversalContainer (Future) – NiceTheo