【问题标题】:How to use OptionT when one of my expressions returns a Future[Seq[User]]当我的一个表达式返回 Future[Seq[User]] 时如何使用 OptionT
【发布时间】:2017-09-13 01:21:05
【问题描述】:

我正在尝试使用 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] 但这不起作用。

【问题讨论】:

  • 我通常做的是将选项变成带有toList 的列表,不确定是否有toSeq

标签: scala scalaz


【解决方案1】:
employees <- OptionT(api.getEmployees(company.id).map(Option(_)))

【讨论】:

  • 返回 Set[Int] 的方法怎么样。我需要用 future 和 liftM[OptionT] 包装它吗?我试过了,但没用。
  • 这种情况下不用提成OptionT,可以在理解中写result = &lt;method returning Set[Int]&gt;
【解决方案2】:

这是您所问内容的完整示例,包括对问题的更新(一种在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))))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-01
    • 2017-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-13
    • 2019-01-07
    相关资源
    最近更新 更多