如何在Scala中有选择地调用函数

时间:2018-10-07 07:29:41

标签: scala

Scala中,我看到了类似于以下示例的示例,其中,如果先前的函数失败,则调用一个函数。但是它是如何工作的呢?例如,下面,如果find成功,则调用map,但是如果find失败,则调用recover。从语法角度看,maprecover都将被调用。

userService.find(id).map { user =>
      Ok(success(user.toJson))
    }.recover { case e =>
      errors.toResult(e) // this returns the appropriate result
    }

3 个答案:

答案 0 :(得分:2)

不是有选择地调用函数,而是总是调用那些函数,但它们的职责是相同的,并且始终transparent

假设我有一个名为Functor的{​​{1}}和MyFunctormap

  • 我将以这样一种方式编写recover:如果当前结果良好(map: A => MyFunctor[B]),然后应用函数,否则返回替代结果
  • A将是recover

示例

Throwable => Myfunctor[B]

现在,您可以链接 class MyFunctor[+A] { import MyFunctor._ def map[B](fn: A => B): MyFunctor[B] = { this match { case Good(a) => Good(fn(a)) case Bad(b) => Bad(b) } } def recover[B >: A](fn: PartialFunction[Throwable, B]): MyFunctor[B] = { this match { case Bad(b) if fn.isDefinedAt(b) => Good(fn(b)) case _ => this } } } object MyFunctor { case class Good[A](data: A) extends MyFunctor[A] case class Bad[A](data: Throwable) extends MyFunctor[A] } maprecovermap都被调用,但是它们会按其应有的方式工作。

recover

答案 1 :(得分:0)

问题是find返回的不是User,而是返回的Future[User]

Future[User]可以是DefaultPromise[User]never

Future的{​​{1}}和map(..)通过recover(..)的{​​重定向到Try的{​​{1}}和map(..) {1}}。两种情况下recover(..)的定义也不同。

Future

https://github.com/scala/scala/blob/2.13.x/src/library/scala/concurrent/Future.scala

transform(..)可以是transform(..)trait Future[+T] extends Awaitable[T] { def map[S](f: T => S)(implicit executor: ExecutionContext): Future[S] = transform(_ map f) def recover[U >: T](pf: PartialFunction[Throwable, U])(implicit executor: ExecutionContext): Future[U] = transform { _ recover pf } def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] //... } private[concurrent] trait Promise[T] extends scala.concurrent.Promise[T] with scala.concurrent.Future[T] { override def transform[S](f: Try[T] => Try[S])(implicit executor: ExecutionContext): Future[S] = { val p = new DefaultPromise[S]() onComplete { result => p.complete(try f(result) catch { case NonFatal(t) => Failure(t) }) } p.future } //... } class DefaultPromise[T] extends AtomicReference[AnyRef](Nil) with Promise[T] { /*...*/ } final object never extends Future[Nothing] { override def transform[S](f: Try[Nothing] => Try[S])(implicit executor: ExecutionContext): Future[S] = this //... } Try[User]Failure[User]的定义在两种情况下都不同。

Success[User]

https://github.com/scala/scala/blob/2.13.x/src/library/scala/util/Try.scala

所以基本上所有情况都可以处理:

  • 当第一次调用成功(然后第二次调用很简单时,您可以在上面看到map(..)的{​​{1}}实现只是recover(..)
  • 第一次呼叫失败但第二次呼叫成功
  • 两个呼叫均失败时

也许您应该详细了解sealed abstract class Try[+T] extends Product with Serializable { def map[U](f: T => U): Try[U] def recover[U >: T](@deprecatedName('f) pf: PartialFunction[Throwable, U]): Try[U] //... } final case class Failure[+T](exception: Throwable) extends Try[T] { override def map[U](f: T => U): Try[U] = this.asInstanceOf[Try[U]] override def recover[U >: T](@deprecatedName('rescueException) pf: PartialFunction[Throwable, U]): Try[U] = try { if (pf isDefinedAt exception) Success(pf(exception)) else this } catch { case NonFatal(e) => Failure(e) } //... } final case class Success[+T](value: T) extends Try[T] { override def map[U](f: T => U): Try[U] = Try[U](f(value)) override def recover[U >: T](@deprecatedName('rescueException) pf: PartialFunction[Throwable, U]): Try[U] = this //... } recover(..)和其他单子,计算的单子链等。

Error Handling With Try

Welcome to the Future

Promises and Futures in Practice

Futures and Promises

答案 2 :(得分:0)

要记住的重要一点是,函数就像其他任何数据一样都是对象。因此,您可以将一个函数传递给另一个函数,而无需实际调用它。将函数作为参数的函数称为higher-order function,有时缩写为<T>

在这种情况下,<dynamic>HOF都是高阶函数;他们采用一个单一的参数,这是一个函数。 maprecover确实都已执行,但是它们不会立即执行您赋予它们的功能。在正常使用中,如果map成功,将调用传递给recover的函数,如果map失败,则将传递传递给find的函数。确切的行为取决于该recover调用返回的对象。