我正在编写使用API的Scala代码,其中对API的调用可以成功,失败或返回异常。我试图让ApiCallResult
monad代表这个,我试图使用Nothing类型,以便将失败和异常情况视为任何{{的子类型1}}类型,类似于None或Nil。到目前为止我的工作似乎有效,但我在ApiCallResult
和map
函数中使用Nothing让我很困惑。以下是flatMap
实现的简要示例:
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
操作成功的结果,但不改变地传递失败和异常。但是,使用Nothing作为输入参数的类型对我没有意义,因为没有Nothing类型的实例。示例中的map
函数的类型为_.size
,如何将其安全地传递给期望String => Int
的内容?这里到底发生了什么?
我还注意到Scala标准库通过让它从Option继承Nothing => U
函数来实现None时避免了这个问题。这只会进一步加深我的感觉,我在某种程度上做了一些可怕的错误。
答案 0 :(得分:2)
有三件事情正在调整以实现这一点,所有这些都与底部类型的协方差和逆变有关:
Nothing
是所有类型的底部类型,例如每种类型都是它的超级。 Function1[-T, +R]
的类型签名,表示它接受T
的超类型的任何类型,并返回R
为超级的任何类型。 ApiCallResult[+R]
表示U
超出R
的任何类型U
都有效。因此任何类型都是super
Nothing
意味着任何参数类型都是有效的,并且您返回Nothing
类型内容的事实是有效的返回类型。
答案 1 :(得分:0)
我建议你不要在大多数时间区分失败和异常。
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
选择结果:
t match {
case Success(s) =>
case Failure(af: ApiFailure) =>
case Failure(x) =>
}