我试图弄清楚如何使用 scalaz7 IO 和 monad 转换器以优雅的纯函数风格编写这段代码,但我无法理解它。
想象一下我有这个简单的 API:
def findUuid(request: Request): Option[String] = ???
def findProfile(uuid: String): Future[Option[Profile]] = redisClient.get[Profile](uuid)
使用这个 API,我可以轻松地使用 OptionT 转换器编写不纯的函数,如下所示:
val profileT = for {
uuid <- OptionT(Future.successful(findUuid(request)))
profile <- OptionT(findProfile(uuid))
} yield profile
val profile: Future[Option[Profile]] = profileT.run
正如您所注意到的 - 此函数包含具有副作用的 findProfile()。我想在 IO monad 内部隔离这种效果并在纯函数之外解释,但不知道如何将它们组合在一起。
def findProfileIO(uuid: String): IO[Future[Option[Profile]]] = IO(findProfile(uuid))
val profileT = for {
uuid <- OptionT(Future.successful(findUuid(request)))
profile <- OptionT(findProfileIO(uuid)) //??? how to put Option inside of the IO[Future[Option]]
} yield profile
val profile = profileT.run //how to run transformer and interpret IO with the unsafePerformIO()???
关于如何完成的任何建议?
最佳答案
IO
更适用于同步效果。 Task
是你想要的更多!
看这个问答:What's the difference between Task and IO in Scalaz?
您可以转换您的 Future
至 Task
然后有一个这样的 API:
def findUuid(request: Request): Option[String] = ???
def findProfile(uuid: String): Task[Option[Profile]] = ???
这是有效的,因为
Task
可以表示同步和异步操作,所以 findUuid
也可以包裹在Task
而不是 IO
.然后你可以将这些包裹在
OptionT
中:val profileT = for {
uuid <- OptionT(Task.now(findUuid(request)))
profile <- OptionT(findProfileIO(uuid))
} yield profile
然后在最后的某个地方你可以运行它:
profileT.run.attemptRun
查看此链接以将 future 转换为任务,反之亦然:Scalaz Task <-> Future
关于scala - IO 和 Future[Option] monad 转换器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44887942/