如何在给定的 Future[Try[Option[A]]] 上应用类型 A => Future[Try[Option[B]]] 的函数?
How to apply function of type A => Future[Try[Option[B]]] on given Future[Try[Option[A]]]?
我想简化函数应用,以便我可以在元素上应用 f 类型的函数:A => Future[Try[Option[B]]]:Future[Try[Option[A]]]
简而言之,我需要以下函数的定义:
def transformation(f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = ???
谢谢。
因为涉及不同的 monad,而且(据我所知)Future[Try[Option]]]
没有 monad 转换器,您需要为每种情况手动映射和匹配:
def transformation[A, B](f: A => Future[Try[Option[B]]])(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = element.flatMap {
case Success(Some(b)) => f(b)
case Success(None) => Future.successful(Success(None))
case Failure(fail) => Future.successful(Failure(fail))
}
如果你必须经常做这样的事情——你可以考虑使用外部库,比如 Emm+cats(或 scalaz),基本上它们提供了自动 monad 转换器:
import $ivy.`org.typelevel::cats:0.9.0`
import $ivy.`com.codecommit::emm-core:0.2.1`
import $ivy.`com.codecommit::emm-cats:0.2.1`
import emm._
import scala.concurrent._
import scala.util._
import cats._
import emm.compat.cats._
import cats.implicits._
type E = Future |: Try |: Option |: Base
import scala.concurrent.ExecutionContext.Implicits.global
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = {
val effect: Emm[E, B] = for {
elTry <- element.liftM[E]
elOption <- elTry.liftM[E]
el <- elOption.liftM[E]
resTry <- f(el).liftM[E]
resOption <- resTry.liftM[E]
res <- resOption.liftM[E]
} yield res
effect.run
}
基本上,当 monad 具有一致的类型时,这就是在正常情况下使用 for
所做的,只需简单地添加 liftM
。简短版本:
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] =
element
.liftM[E]
.flatMap(_.liftM[E]).flatMap(_.liftM[E]) //unpack A
.map(f) //apply transformation
.flatMap(_.liftM[E]).flatMap(_.liftM[E]).flatMap(_.liftM[E]) //unpack B
.run
您可能想要使用 monad 转换器。你的类型
Future[Try[Option[A]]]
等同于
OptionT[EitherT[Future, Throwable, ?], A]
(?
语法来自 kind-projector
编译器插件。)
让我们一步一步地观察这个。
首先,Try[A]
等价于析取 Throwable \/ A
(其中 \/
只是 scalaz 等价于 Either
)。我们可以通过 toDisjunction
, fromDisjunction
在它们之间进行翻译。这给了我们
Future[Try[Option[A]]]
等同于
Future[Throwable \/ Option[A]]
接下来,知道F[A \/ B]
是EitherT[F, A, B]
的definition,我们得到上面等价于
EitherT[Future, Throwable, Option[A]]
最后,注意到 OptionT[F, A]
的 definition 是 F[Option[A]]
,将 F[?]
设为 EitherT[Future, Throwable, ?]
我们得出结论,上面等价于
OptionT[EitherT[Future, Throwable, ?], A]
我们从这个表示中得到了什么?
现在你的 transformation
函数就是 flatMap
.
type Effect[A] = OptionT[EitherT[Future, Throwable, ?], A]
def transformation1[A, B](f: A => Effect[B])(element: Effect[A])
(implicit ec: ExecutionContext): Effect[B] =
element.flatMap(f)
要使用您的原始签名(即与 Future[Try[Option[A]]]
一起使用的签名)获得 transformation
函数,我们需要定义与 Effect
和 Effect
之间的转换方法。
def toEffect[A](a: Future[Try[Option[A]]])(implicit ec: ExecutionContext): Effect[A] = ???
def fromEffect[A](a: Effect[A])(implicit ec: ExecutionContext): Future[Try[Option[A]]] = ???
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]])
(implicit ec: ExecutionContext): Future[Try[Option[B]]] =
fromEffect(for {
a <- toEffect(element)
b <- toEffect(f(a))
} yield b)
但也许您可能希望在整个应用程序中使用 monad 转换器表示,而不是来回转换。
完整代码
这是完整的代码,包括导入。我用 scalaz 7.3.0-M10 测试了它。
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
import scalaz._
import scalaz.std.scalaFuture._
import scalaz.std.`try`._
import scalaz.syntax.std.`try`._
type Effect[A] = OptionT[EitherT[Future, Throwable, ?], A]
def toEffect[A](a: Future[Try[Option[A]]])(implicit ec: ExecutionContext): Effect[A] = {
val b: Future[Throwable \/ Option[A]] = a.map(_.toDisjunction)
val c: EitherT[Future, Throwable, Option[A]] = EitherT(b)
val d: OptionT[EitherT[Future, Throwable, ?], A] = OptionT[EitherT[Future, Throwable, ?], A](c)
d
}
def fromEffect[A](a: Effect[A])(implicit ec: ExecutionContext): Future[Try[Option[A]]] =
a.run.run.map(fromDisjunction(_))
def transformation1[A, B](f: A => Effect[B])(element: Effect[A])
(implicit ec: ExecutionContext): Effect[B] =
element.flatMap(f)
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]])
(implicit ec: ExecutionContext): Future[Try[Option[B]]] =
fromEffect(for {
a <- toEffect(element)
b <- toEffect(f(a))
} yield b)
我想简化函数应用,以便我可以在元素上应用 f 类型的函数:A => Future[Try[Option[B]]]:Future[Try[Option[A]]]
简而言之,我需要以下函数的定义:
def transformation(f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = ???
谢谢。
因为涉及不同的 monad,而且(据我所知)Future[Try[Option]]]
没有 monad 转换器,您需要为每种情况手动映射和匹配:
def transformation[A, B](f: A => Future[Try[Option[B]]])(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = element.flatMap {
case Success(Some(b)) => f(b)
case Success(None) => Future.successful(Success(None))
case Failure(fail) => Future.successful(Failure(fail))
}
如果你必须经常做这样的事情——你可以考虑使用外部库,比如 Emm+cats(或 scalaz),基本上它们提供了自动 monad 转换器:
import $ivy.`org.typelevel::cats:0.9.0`
import $ivy.`com.codecommit::emm-core:0.2.1`
import $ivy.`com.codecommit::emm-cats:0.2.1`
import emm._
import scala.concurrent._
import scala.util._
import cats._
import emm.compat.cats._
import cats.implicits._
type E = Future |: Try |: Option |: Base
import scala.concurrent.ExecutionContext.Implicits.global
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = {
val effect: Emm[E, B] = for {
elTry <- element.liftM[E]
elOption <- elTry.liftM[E]
el <- elOption.liftM[E]
resTry <- f(el).liftM[E]
resOption <- resTry.liftM[E]
res <- resOption.liftM[E]
} yield res
effect.run
}
基本上,当 monad 具有一致的类型时,这就是在正常情况下使用 for
所做的,只需简单地添加 liftM
。简短版本:
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] =
element
.liftM[E]
.flatMap(_.liftM[E]).flatMap(_.liftM[E]) //unpack A
.map(f) //apply transformation
.flatMap(_.liftM[E]).flatMap(_.liftM[E]).flatMap(_.liftM[E]) //unpack B
.run
您可能想要使用 monad 转换器。你的类型
Future[Try[Option[A]]]
等同于
OptionT[EitherT[Future, Throwable, ?], A]
(?
语法来自 kind-projector
编译器插件。)
让我们一步一步地观察这个。
首先,Try[A]
等价于析取 Throwable \/ A
(其中 \/
只是 scalaz 等价于 Either
)。我们可以通过 toDisjunction
, fromDisjunction
在它们之间进行翻译。这给了我们
Future[Try[Option[A]]]
等同于
Future[Throwable \/ Option[A]]
接下来,知道F[A \/ B]
是EitherT[F, A, B]
的definition,我们得到上面等价于
EitherT[Future, Throwable, Option[A]]
最后,注意到 OptionT[F, A]
的 definition 是 F[Option[A]]
,将 F[?]
设为 EitherT[Future, Throwable, ?]
我们得出结论,上面等价于
OptionT[EitherT[Future, Throwable, ?], A]
我们从这个表示中得到了什么?
现在你的 transformation
函数就是 flatMap
.
type Effect[A] = OptionT[EitherT[Future, Throwable, ?], A]
def transformation1[A, B](f: A => Effect[B])(element: Effect[A])
(implicit ec: ExecutionContext): Effect[B] =
element.flatMap(f)
要使用您的原始签名(即与 Future[Try[Option[A]]]
一起使用的签名)获得 transformation
函数,我们需要定义与 Effect
和 Effect
之间的转换方法。
def toEffect[A](a: Future[Try[Option[A]]])(implicit ec: ExecutionContext): Effect[A] = ???
def fromEffect[A](a: Effect[A])(implicit ec: ExecutionContext): Future[Try[Option[A]]] = ???
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]])
(implicit ec: ExecutionContext): Future[Try[Option[B]]] =
fromEffect(for {
a <- toEffect(element)
b <- toEffect(f(a))
} yield b)
但也许您可能希望在整个应用程序中使用 monad 转换器表示,而不是来回转换。
完整代码
这是完整的代码,包括导入。我用 scalaz 7.3.0-M10 测试了它。
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Try
import scalaz._
import scalaz.std.scalaFuture._
import scalaz.std.`try`._
import scalaz.syntax.std.`try`._
type Effect[A] = OptionT[EitherT[Future, Throwable, ?], A]
def toEffect[A](a: Future[Try[Option[A]]])(implicit ec: ExecutionContext): Effect[A] = {
val b: Future[Throwable \/ Option[A]] = a.map(_.toDisjunction)
val c: EitherT[Future, Throwable, Option[A]] = EitherT(b)
val d: OptionT[EitherT[Future, Throwable, ?], A] = OptionT[EitherT[Future, Throwable, ?], A](c)
d
}
def fromEffect[A](a: Effect[A])(implicit ec: ExecutionContext): Future[Try[Option[A]]] =
a.run.run.map(fromDisjunction(_))
def transformation1[A, B](f: A => Effect[B])(element: Effect[A])
(implicit ec: ExecutionContext): Effect[B] =
element.flatMap(f)
def transformation[A, B](f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]])
(implicit ec: ExecutionContext): Future[Try[Option[B]]] =
fromEffect(for {
a <- toEffect(element)
b <- toEffect(f(a))
} yield b)