为什么创建参数类型为“Nothing => U”的映射函数似乎有效?

Why does creating a map function whose parameter is of type `Nothing => U` appear to work?

我正在编写使用 API 的 Scala 代码,其中对 API 的调用可能成功、失败或 return 异常。我正在尝试制作一个 ApiCallResult monad 来表示这一点,并且我正在尝试使用 Nothing 类型,以便可以将失败和异常情况视为任何 ApiCallResult 类型的子类型,类似于 None 或 Nil。到目前为止,我所做的一切似乎都有效,但我在 mapflatMap 函数中使用 Nothing 让我感到困惑。这是我仅使用 map 实现的简化示例:

  sealed trait ApiCallResult[+T] {
    def map[U]( f: T => U ): ApiCallResult[U]
  }

  case class ResponseException(exception: APICallExceptionReturn) extends ApiCallResult[Nothing] {
    override def map[U]( f: Nothing => U ) = this
  }

  case object ResponseFailure extends ApiCallResult[Nothing] {
    override def map[U]( f: Nothing => U ) = ResponseFailure
  }

  case class ResponseSuccess[T](payload: T) extends ApiCallResult[T] {
    override def map[U]( f: T => U ) = ResponseSuccess( f(payload) )
  }

  val s: ApiCallResult[String] = ResponseSuccess("foo")
  s.map( _.size )  // evaluates to ResponseSuccess(3)

  val t: ApiCallResult[String] = ResponseFailure
  t.map( _.size )  // evaluates to ResponseFailure

所以它似乎按照我预期的方式工作 map 对成功结果进行操作,但传递失败和异常并保持不变。但是,使用 Nothing 作为输入参数的类型对我来说毫无意义,因为没有 Nothing 类型的实例。示例中的 _.size 函数具有类型 String => Int,如何将它安全地传递给需要 Nothing => U 的对象?这到底是怎么回事?

我还注意到 Scala 标准库在实现 None 时通过让它从 Option 继承 map 函数来避免这个问题。这只会加深我的感觉,我不知何故做了一些可怕的错误。

我建议你大多数时候不需要区分失败和异常。

type ApiCallResult[+T] = Try[T]
case class ApiFailure() extends Throwable

val s: ApiCallResult[String] = Success("this is a string")

s.map(_.size)

val t: ApiCallResult[String] = Failure(new ApiFailure)
t.map(_.size)

拾取失败,用match到select结果:

t match {
case Success(s) =>
case Failure(af: ApiFailure) =>
case Failure(x) =>
}

三件事正在对齐以实现这一点,所有这些都与面对底部类型的协变和逆变有关:

  1. Nothing 是所有类型的底部类型,例如每种类型都是它的超级。
  2. Function1[-T, +R] 的类型签名,意味着它接受任何类型 T 的超类型和 returns 任何 R 的超类型。
  3. 类型 ApiCallResult[+R] 表示任何 U 类型 RU 的父类型都是有效的。

所以任何类型都是 Nothingsuper 意味着任何参数类型都是有效的,而且你 return 在 Nothing 周围输入的东西是有效的 return类型。