我想简化函数应用程序,以便我可以应用f类型的函数:A =>未来[尝试[选项[B]]]元素:未来[尝试[选项[A]]]
简而言之,我需要一个以下功能的定义:
def transformation(f: A => Future[Try[Option[B]]])
(element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = ???
感谢。
答案 0 :(得分:3)
因为涉及不同的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))
}
答案 1 :(得分:2)
您可能想要使用 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]
的{{3}},我们知道上述内容相当于
EitherT[Future, Throwable, Option[A]]
最后,注意OptionT[F, A]
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)
。
transformation
要使用原始签名(即使用Future[Try[Option[A]]]
的签名)获取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)
之间的转换方法。
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)
但也许不是来回转换,你可能想在整个应用程序中使用monad变换器表示。
这是完整的代码,包括导入。我用scalaz 7.3.0-M10进行了测试。
{{1}}
答案 2 :(得分:0)
如果你经常做这样的事情 - 你可能会考虑使用外部库,比如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