Я пишу код Scala, который использует API, в котором вызовы API могут либо преуспеть, либо пропустить, либо возвратить исключение. Я пытаюсь создать монаду ApiCallResult
, чтобы представить это, и я пытаюсь использовать тип Nothing, чтобы случаи сбоя и исключений можно рассматривать как подтип любого типа ApiCallResult
, аналогичный None или Nil. До сих пор я работаю, но мое использование Nothing в функциях map
и flatMap
меня смущает. Вот упрощенный пример того, что я только с map
реализации:Почему создание функции карты, параметр которой имеет тип `Nothing => U`, работает?
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, позволяя ей наследовать функцию map
из опции. Это только усиливает мой смысл, что я как-то делаю что-то ужасно неправильно.
Вам действительно нужно различать ошибки и исключения? Если вы повторно откажетесь от «ApiFailure», наследуя «Throwable», вы можете теперь перестроить свой ApiCallResult в «Try [+ T]». Это позволяет продолжить распространение ошибок и ошибок, без повторной реализации. Когда вам нужно отличать неудачи от исключений, вы можете сопоставлять совпадение на 'ApiFailure' и т. Д. –
@BobDalgleish Я думаю, что подход или их варианты определенно заслуживают рассмотрения, но я все равно хотел бы понять, является ли использование Nothing здесь действительно правильно и почему. –
Я раскрыл свои комментарии в ответе ниже. Я изучал, как 'Nothing' используется как' Try', так и 'List' /' Nil'. Ваш подход приводит вас к неприятностям, когда вам нужно использовать «карту» «ничего».Если вы переместите 'map' для' ResponseFailure' до родительского признака, вы можете реализовать 'map' так же, как' List'. С другой стороны, у вас должен быть параметр типа для 'ResponseException' таким же образом, что' Try' реализует 'Failure'. –