Voici un exemple complet pour ce que vous demandez, y compris la mise à jour à la question (une méthode qui retourne Seq[Employee]
, en dehors du Future
contexte):
import scala.concurrent.ExecutionContext.Implicits.global
import cats.data.OptionT
import cats.implicits._
case class Employee(x : String)
case class User(x : String)
case class Company(x : String)
case class CompanyProfile(user : User, company: Company, employees: Seq[Employee])
def emptyEmployees : Future[Seq[Employee]] = Future.successful(Seq())
def nonEmptyEmployees : Future[Seq[Employee]] = Future.successful(Seq(Employee("Test1"),Employee("Test2")))
def user : Future[Option[User]] = Future.successful(Some(User("user1")))
def company : Future[Option[Company]] = Future.successful(Some(Company("company1")))
val res: OptionT[Future, CompanyProfile] = for{
user <- OptionT(user)
company <- OptionT(company)
employeesOne <- OptionT(nonEmptyEmployees.map(Option(_))) // Here you wrap the `Seq[Employee]` into an `Option`
employeesTwo <- OptionT.pure[Future, Seq[Employee]](employees) // Here you lift the `Seq[Employee]` into the context of `OptionT[Future, Seq[Employee]]`
} yield CompanyProfile(user, company, employeesOne ++ employeesTwo)
Await.result(res.value, Duration.Inf)
// Option[CompanyProfile] = Some(CompanyProfile(User(user1),Company(company1),List(Employee(Test1), Employee(Test2), Employee(Test3), Employee(Test4))))
A noter cependant que, comme @ Zhang Liu mentionné dans les commentaires à sa réponse, vous n'avez pas à l'ajouter au contexte OptionT
. Vous appelez probablement cette méthode depuis le bloc yield
.
Mise à jour
Un peu sans rapport avec votre question, mais utile de se rappeler que cela aide les scénarios de test comme le vôtre, vous pouvez aussi abstrait le Future
loin, il est donc plus facile de tester votre code sans se soucier de ExecutionContexts
etc.
Mon code précédent devient alors:
def emptyEmployeesM[M[_]](implicit m : Monad[M]) : M[Seq[Employee]] =
m.pure(Seq.empty)
def nonEmptyEmployeesM[M[_]](implicit m : Monad[M]) : M[Seq[Employee]] =
m.pure(Seq(Employee("Test1"),Employee("Test2")))
def employees : Seq[Employee] = Seq(Employee("Test3"),Employee("Test4"))
def userM[M[_]](implicit m : Monad[M]) : M[Option[User]] = m.pure(Some(User("user1")))
def companyM[M[_]](implicit m : Monad[M]) : M[Option[Company]] = m.pure(Some(Company("company1")))
def companyProfile[M[_]](implicit m : Monad[M]): OptionT[M, CompanyProfile] = for{
user <- OptionT(userM[M])
company <- OptionT(companyM[M])
employeesOne <- OptionT(Functor[M].map(nonEmptyEmployeesM[M])(Option(_)))
employeesTwo <- OptionT.pure[M, Seq[Employee]](employees)
} yield CompanyProfile(user, company, employeesOne ++ employeesTwo)
// Here we still use Future
val res1 = Await.result(companyProfile[Future].value, Duration.Inf) // Option[CompanyProfile] = Some(CompanyProfile(User(user1),Company(company1),List(Employee(Test1), Employee(Test2), Employee(Test3), Employee(Test4))))
// However here we can use another Monad, in this case Id (which is simply a type alias to itself), which allows to test more easily your companyProfile method.
val res2 = companyProfile[Id].value // Option[CompanyProfile] = Some(CompanyProfile(User(user1),Company(company1),List(Employee(Test1), Employee(Test2), Employee(Test3), Employee(Test4))))
Ce que je fais habituellement est de tourner les options dans les listes avec 'toList' pas sûr qu'il ya un' toSeq'. – pedrofurla