我目前正在做一个关于函数式编程的演讲,遇到了以下问题。
函数式编程旨在将"什么"与"如何"分开,或者更准确地说,将计算的声明与其解释分开。这就是为什么这种范式的主要焦点之一是使用可组合的数据结构来表示计算,而不对它们的执行方式做出任何假设。例如:
// Represents a computation that may fail
case class Unsafe[A,B](run: A => B)
// ...
val readIntFromFile: Unsafe[String, Int] = Unsafe { filePath => /* ... */ }
interpret(readIntFromFile)
// Interpreter
def interpret(u: Unsafe[String, Int]): Unit = {
try {
u.run("path/to/file")
} catch {
case e => /* ... */
}
}
这似乎是有道理的,因为副作用应该只在计算执行期间执行,而不是在其声明期间执行。问题在于,在 Scala 中,许多数据结构似乎都打破了这一规则:
object Try {
/** Constructs a `Try` using the by-name parameter. This
* method will ensure any non-fatal exception is caught and a
* `Failure` object is returned.
*/
def apply[T](r: => T): Try[T] =
try Success(r) catch {
case NonFatal(e) => Failure(e)
}
}
与Futures
相同:
/** Starts an asynchronous computation and returns a `Future` object with the result of that computation.
*
* The result becomes available once the asynchronous computation is completed.
*
* @tparam T the type of the result
* @param body the asynchronous computation
* @param executor the execution context on which the future is run
* @return the `Future` holding the result of the computation
*/
def apply[T](body: =>T)(implicit @deprecatedName('execctx) executor: ExecutionContext): Future[T] = impl.Future(body)
所以,我现在想知道,Try
和Future
真的是透明的吗?如果没有,那么如何在不依赖Success
和Failure
的情况下处理错误情况?
只要不使用副作用,尝试在引用上是透明的。 Try 的目的不是控制副作用,而是处理可能的异常。
如果您需要以纯粹的方式控制副作用,您可以使用 Cats 和 Scalaz 等库中的任务或 IO 类型。
Future
绝对不是RT,因为这两个块并不等价:
-
两个期货并行执行:
val fa: Future[Int] = service.call val fb: Future[Int] = service.call for { a <- fa; b <- fb } yield a + b
-
两个期货依次执行:
for { a <- service.call; b <- service.call } yield a + b
另一方面,Try
是。处理错误的正确功能方法是将 Either[ErrorDescription, A]
用于返回A
但可能会失败的方法(您可以将 type ErrorDescription = Throwable
用于等效于 scala.util.Try
!