return 早期的 Unit-returning 函数在 Scala 中

return early in Unit-returning function in Scala

假设我在 Scala 中有一个函数 return 是一个单位值。该函数在开始时进行了一定的测试,并得出结论,它可以停止,即 return,已经。只放一个 return 语句(不带任何东西)并离开函数是否安全?

命令式副作用代码在 Scala 中大多是非惯用的,因此您找不到任何关于 如何 在 Scala 中编写命令式副作用代码的指导。

听起来你在谈论典型的保护条款风格

def procedureWithGuard(): Unit =
  if nothingToDo then return
  doTheExpensiveThing()

procedureWithGuard()

这是完美的命令式风格。它只是不完美的 Scala 风格,但这不是因为早期 return,而是因为使用副作用 一般 .

请注意,Scala 3 中有一个变化:过去,嵌套匿名函数中的 returnreturn 来自最近的词法封闭方法,即 in

def returnFromNestedFunction: Boolean =
  someCollection.foreach(x => if x % 2 == 0 then return true)
  false

两个 returns 都会 return 来自方法 returnFromNestedFunction,即使第一个 return 实际上在某些自动生成的 apply 方法中Function1[T, Boolean] 的实例。这需要编译器跳过一些障碍,因为 Scala 支持的大多数目标平台(JVM、ECMAScript 和过去的 .NET)根本不支持从一个方法到另一个方法的 returning,它们也不支持 GOTO 跨方法。

Scala 中的实现方式是将内部 return 编译成特殊异常的 throw ,并在 returns ,编译器合成了一个对应的catch。但是,这有两个问题:

  1. 在所有支持的平台上,抛出和捕获异常都很慢,比return慢很多。因此,虽然它 看起来 代码具有 return 的性能(实际上是免费的),但它实际上具有异常的性能(非常慢) .
  2. 您可能会因为无意中捕获编译器生成的异常的全能异常处理程序而意外破坏代码。

出于这个原因,return在 Scala 3 中不推荐使用嵌套匿名函数。取而代之的是 there is a library,这使得使用异常抛出技巧变得容易 显式,这样就没有隐藏的性能成本(该库使异常抛出技巧易于使用,但它并没有隐藏涉及异常的事实)并且您不会意外捕获您没有捕获的隐藏异常不知道(因为首先没有隐藏异常)。

import scala.util.control.NonLocalReturns.*

def returnFromNestedFunction = returning {
  someCollection.foreach(x => if x % 2 == 0 then throwReturn(true))
  false
}

请注意,您 return 从嵌套的匿名函数中乍一看并不总是很明显。特别是,for-comprehensions 脱糖为 foreach(如果没有 yield)、map(如果有 yield)或 flatMap(如果有多个生成器)和withFilter(如果有if)。比如这段代码其实和上面一样,只是没有明显可见的匿名函数:

import scala.util.control.NonLocalReturns.*

def returnFromNestedFunction = returning {
  for x <- someCollection do
    if x % 2 == 0 then throwReturn(true)
  false
}