为什么创建参数类型为“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。到目前为止,我所做的一切似乎都有效,但我在 map
和 flatMap
函数中使用 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) =>
}
三件事正在对齐以实现这一点,所有这些都与面对底部类型的协变和逆变有关:
Nothing
是所有类型的底部类型,例如每种类型都是它的超级。
Function1[-T, +R]
的类型签名,意味着它接受任何类型 T
的超类型和 returns 任何 R
的超类型。
- 类型
ApiCallResult[+R]
表示任何 U
类型 R
是 U
的父类型都是有效的。
所以任何类型都是 Nothing
的 super
意味着任何参数类型都是有效的,而且你 return 在 Nothing
周围输入的东西是有效的 return类型。
我正在编写使用 API 的 Scala 代码,其中对 API 的调用可能成功、失败或 return 异常。我正在尝试制作一个 ApiCallResult
monad 来表示这一点,并且我正在尝试使用 Nothing 类型,以便可以将失败和异常情况视为任何 ApiCallResult
类型的子类型,类似于 None 或 Nil。到目前为止,我所做的一切似乎都有效,但我在 map
和 flatMap
函数中使用 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) =>
}
三件事正在对齐以实现这一点,所有这些都与面对底部类型的协变和逆变有关:
Nothing
是所有类型的底部类型,例如每种类型都是它的超级。Function1[-T, +R]
的类型签名,意味着它接受任何类型T
的超类型和 returns 任何R
的超类型。- 类型
ApiCallResult[+R]
表示任何U
类型R
是U
的父类型都是有效的。
所以任何类型都是 Nothing
的 super
意味着任何参数类型都是有效的,而且你 return 在 Nothing
周围输入的东西是有效的 return类型。