我正在尝试使用optionT,我的函数返回Future[Option[T]]
我的一个电话会回复Future[Seq[T]]
,我应该如何处理这个案例?
for {
user <- OptionT(api.getUser(123))
company <- OptionT(api.getCompany(user.companyId))
employees <- api.getEmployees(company.id) // returns Future[Seq[Employee]]
} yield CompanyProfile(user, company, employees)
更新的 我还有一个返回Set [Int]的方法。不知道怎么把它包装成一个OptionT,我试过了:
.liftM[OptionT]
但是那不起作用。
答案 0 :(得分:1)
employees <- OptionT(api.getEmployees(company.id).map(Option(_)))
答案 1 :(得分:1)
以下是您要问的完整示例,包括问题的更新(在Seq[Employee]
上下文之外返回Future
的方法):
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在评论中提到的那样,你没有必须将其添加到OptionT
上下文中。您可能会在yield
块中调用该方法。
<强>更新强>
与您的问题有点无关,但有助于记住,因为它有助于测试像您这样的场景,您也可以抽象Future
,所以更容易测试代码而不用担心ExecutionContexts
等。< / p>
我之前的代码将成为:
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))))