这是您所问内容的完整示例,包括对问题的更新(一种在Future 上下文之外返回Seq[Employee] 的方法):
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))))
但是请注意,正如@Zhang Liu 在 cmets 中提到的那样,您不必必须将其添加到 OptionT 上下文中。您可能会从 yield 块中调用该方法。
更新
与您的问题有点无关,但记住它很有用,因为它有助于测试像您这样的场景,您还可以将Future 抽象出来,因此更容易测试您的代码而不必担心ExecutionContexts 等。
我之前的代码会变成:
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))))