如何应用A =>类型的函数未来[尝试[选项[B]]]给定的未来[尝试[选项[A]]]?

时间:2017-04-04 10:30:59

标签: scala functional-programming scalaz

我想简化函数应用程序,以便我可以应用f类型的函数:A =>未来[尝试[选项[B]]]元素:未来[尝试[选项[A]]]

简而言之,我需要一个以下功能的定义:

def transformation(f: A => Future[Try[Option[B]]])
                       (element: Future[Try[Option[A]]]): Future[Try[Option[B]]] = ???

感谢。

3 个答案:

答案 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