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

在Scala中如何有选择地调用函数,scala,Scala,在Scala中,我看到了如下示例,其中如果前一个函数失败,将调用函数。但是它是如何工作的呢?例如,在下面,如果find成功,则调用map,但如果find失败,则调用recover。从语法的角度来看,map和recover都将被调用 userService.find(id).map { user => Ok(success(user.toJson)) }.recover { case e => errors.toResult(e) // this ret

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

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

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

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

Future
map(..)
recover(..)
重定向到
尝试
map(..)
恢复(..)
通过
Future
转换(..)
。对于这两种情况,
变换(…)
的定义是不同的

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]
。定义了
map(..)
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(..)
    for
    success
    的实现非常简单
  • 当第一次呼叫失败但第二次呼叫成功时
  • 当两个调用都失败时
也许你应该阅读更多关于
Try
Future
和其他单子,计算的单子链等的内容


不是有选择地调用函数,而是始终调用这些函数,但它们的职责始终相同

假设我有自己的
Functor
名为
MyFunctor
,带有
map
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]

  }
现在,您可以在
map
recover
上进行链接。调用了
map
recover
,但它们做了它们应该做的事情

val good = Good(1000).map(_ * 2).recover { case a: Throwable => 11 }
println(good) //Good(2000)

val bad = MyFunctor.Bad[Int](new Exception("error")).map(a => a * 2).recover {
  case e: Throwable => 8
}

println(bad) // Good(8)

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


在这种情况下,
map
recover
都是高阶函数;它们只接受一个作为函数的参数。确实执行了
map
recover
,但它们不会立即执行您赋予它们的函数。在正常使用中,如果
查找
成功,将调用传递给
映射
的函数;如果
查找
失败,将调用传递给
恢复
的函数。确切的行为取决于该
find
调用返回的对象。

纠正我的错误,我认为find(id)将返回您未来的[Option[user]]?或者find方法是用户定义的?返回未来[用户]或异常!我只是从SO中挑选了一个例子。但一般来说,在Scala中到处都可以看到这样的函数组合,例如map和recover in future,其中map在future返回值时调用,recover在future抛出异常时调用。如何创建这样的函数?好的,通过代码我可以推断find(id)将返回Future[User]或Future[Exception],现在您必须执行。map或onComplete或任何在Future完成后调用的回调,那么您在这段代码中有case Success或Failure,我们在失败时使用recover。