Scala IO和未来[选项]单声道变压器

Scala IO和未来[选项]单声道变压器,scala,scalaz,monad-transformers,io-monad,Scala,Scalaz,Monad Transformers,Io Monad,我正试图弄明白如何使用scalaz7 IO和monad transformers以优雅的纯函数风格编写这段代码,但就是想不通 想象一下,我有一个简单的API: def findUuid(request: Request): Option[String] = ??? def findProfile(uuid: String): Future[Option[Profile]] = redisClient.get[Profile](uuid) 使用此API,我可以很容易地使用OptionT tran

我正试图弄明白如何使用scalaz7 IO和monad transformers以优雅的纯函数风格编写这段代码,但就是想不通

想象一下,我有一个简单的API:

def findUuid(request: Request): Option[String] = ???
def findProfile(uuid: String): Future[Option[Profile]] = redisClient.get[Profile](uuid)
使用此API,我可以很容易地使用OptionT transformer编写不纯函数,如下所示:

val profileT = for {
  uuid <- OptionT(Future.successful(findUuid(request)))
  profile <- OptionT(findProfile(uuid))
} yield profile
val profile: Future[Option[Profile]] = profileT.run
def findUuid(request: Request): Option[String] = ??? 
def findProfile(uuid: String): Task[Option[Profile]] = ???
val profileT=for{

uuid
IO
意味着更多的同步效果。
任务
更多的是你想要的! 请参见此问题和答案:

您可以将您的
未来
转换为
任务
,然后使用如下API:

val profileT = for {
  uuid <- OptionT(Future.successful(findUuid(request)))
  profile <- OptionT(findProfile(uuid))
} yield profile
val profile: Future[Option[Profile]] = profileT.run
def findUuid(request: Request): Option[String] = ??? 
def findProfile(uuid: String): Task[Option[Profile]] = ???
这是因为
Task
可以表示同步和异步操作,因此
findUuid
也可以包装在
Task
中,而不是
IO

然后您可以将它们包装在
选项中

val profileT = for {
  uuid <- OptionT(Task.now(findUuid(request)))
  profile <- OptionT(findProfileIO(uuid))
} yield profile

查看此链接可将未来转换为任务,反之亦然:

以这段代码结束,认为它可能对某些人有用(第2.6节)

控制器的方法是一个纯函数,因为任务评估发生在PureAction ActionBuilder的控制器之外。感谢Luka的回答

尽管在剧本2.6中仍在挣扎于新的动作组合范式,但这是另一个故事

FrontendController.scala:

def index = PureAction.pure { request =>
  val profileOpt = (for {
    uuid <- OptionT(Task.now(request.cookies.get("uuid").map(t => uuidKey(t.value))))
    profile <- OptionT(redis.get[Profile](uuid).asTask)
  } yield profile).run
  profileOpt.map { profileOpt =>
    Logger.info(profileOpt.map(p => s"User logged in - $p").getOrElse("New user, suggesting login"))
    Ok(views.html.index(profileOpt))
  }
}
转换器。scala

任务->未来和未来->任务隐式转换器

implicit class FuturePimped[+T](root: => Future[T]) {
  import scalaz.Scalaz._
  def asTask(implicit ec: ExecutionContext): Task[T] = {
    Task.async { register =>
      root.onComplete {
        case Success(v) => register(v.right)
        case Failure(ex) => register(ex.left)
      }
    }
  }
}

implicit class TaskPimped[T](root: => Task[T]) {
  import scalaz._
  val p: Promise[T] = Promise()
  def asFuture: Future[T] = {
    root.unsafePerformAsync {
      case -\/(ex) => p.failure(ex); ()
      case \/-(r) => p.success(r); ()
    }
    p.future
  }
}

Thx@luka jacobowitz有问题*我在我的剧本Action.async中使用了这段代码,所以我必须返回Future[Result]将任务转换为Scala。将来意味着任务终止。一旦它终止,Glay的动作就变成同步。你知道如何将任务转换为没有终止的未来吗?*进一步。你认为IO Mad是一个延迟的计算吗?如果它是这样的话,它意味着原始的FrimePrrices:未来也是懒惰的,它也是计算。在ExecutionContext中延迟?在这种情况下,可能没有必要将Future包装到IO中-此函数已经是纯函数了?Future的问题在于它不是懒惰的。它将执行Futures主体内部定义的任何副作用,默认情况下使其成为不纯函数。有关更多信息,请参阅此处:我的建议是使用T在任何地方提问,并在必要时转换为未来或从未来转换:)谢谢你。昨晚花了很多时间研究未来/任务比较,也请阅读这篇reddit文章。
implicit class FuturePimped[+T](root: => Future[T]) {
  import scalaz.Scalaz._
  def asTask(implicit ec: ExecutionContext): Task[T] = {
    Task.async { register =>
      root.onComplete {
        case Success(v) => register(v.right)
        case Failure(ex) => register(ex.left)
      }
    }
  }
}

implicit class TaskPimped[T](root: => Task[T]) {
  import scalaz._
  val p: Promise[T] = Promise()
  def asFuture: Future[T] = {
    root.unsafePerformAsync {
      case -\/(ex) => p.failure(ex); ()
      case \/-(r) => p.success(r); ()
    }
    p.future
  }
}