2017-01-06 2 views
2

Я пишу код 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 из опции. Это только усиливает мой смысл, что я как-то делаю что-то ужасно неправильно.

+0

Вам действительно нужно различать ошибки и исключения? Если вы повторно откажетесь от «ApiFailure», наследуя «Throwable», вы можете теперь перестроить свой ApiCallResult в «Try [+ T]». Это позволяет продолжить распространение ошибок и ошибок, без повторной реализации. Когда вам нужно отличать неудачи от исключений, вы можете сопоставлять совпадение на 'ApiFailure' и т. Д. –

+0

@BobDalgleish Я думаю, что подход или их варианты определенно заслуживают рассмотрения, но я все равно хотел бы понять, является ли использование Nothing здесь действительно правильно и почему. –

+0

Я раскрыл свои комментарии в ответе ниже. Я изучал, как 'Nothing' используется как' Try', так и 'List' /' Nil'. Ваш подход приводит вас к неприятностям, когда вам нужно использовать «карту» «ничего».Если вы переместите 'map' для' ResponseFailure' до родительского признака, вы можете реализовать 'map' так же, как' List'. С другой стороны, у вас должен быть параметр типа для 'ResponseException' таким же образом, что' Try' реализует 'Failure'. –

ответ

2

Три вещи выравнивания, чтобы это произошло, все, что связанно с ковариацией и контрвариацией в лице нижний тип:

  1. Nothing является нижним типом для всех типов, например каждый тип - это супер.
  2. Типовая подпись Function1[-T, +R], что означает, что она принимает любой тип, который является супер T и возвращает любой тип, для которого R является супер.
  3. Тип ApiCallResult[+R] означает любой тип U, для которого R - это супер U.

Таким образом, любой тип является super из Nothing означает как любой аргумент типа является действительным, и тот факт, что вы что-то вернуть напечатанный вокруг Nothing является допустимым типом возврата.

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) => 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^