не могу связать [SttpBackend [Попробуйте, ничего]] - PullRequest
0 голосов
/ 25 января 2019

Я хочу использовать в своем приложении библиотеку sttp с guice (с оболочкой скалагов).Но, кажется, не так просто правильно связать такие вещи, как SttpBackend[Try, Nothing]

SttpBackend.scala

Try[_] и Try[AnyRef] показать некоторые другие ошибки, но все жепонятия не имею, как это должно быть сделано правильно

ошибка, которую я получил:

kinds of the type arguments (scala.util.Try) do not conform to the expected kinds of the type parameters (type T).
[error] scala.util.Try's type parameters do not match type T's expected parameters:
[error] class Try has one type parameter, but type T has none
[error]         bind[SttpBackend[Try, Nothing]].toProvider[SttpBackendProvider]
[error]    `         ^

SttpBackendProvider выглядит так:

def get: SttpBackend[Try, Nothing] = TryHttpURLConnectionBackend(opts)

полный пример в scastie интересно, что версия scalaguice 4.1.0 показывает эту ошибку, но последняя 4.2.2 показывает ошибку внутри нее с преобразованием Nothing в JavaType

1 Ответ

0 голосов
/ 29 января 2019

Я полагаю, что вы столкнулись с двумя различными ошибками в Scala-Guice, одна из которых еще не исправлена ​​(и, возможно, даже еще не отправлена).

Чтобы описать эти проблемы, мне нужно кратко представить, как Guice иСкала-Guice работа.По сути, то, что делает Guice, - это отображение типа в метод фабрики для объекта этого типа.Для поддержки некоторых расширенных типов функций они сопоставляются с некоторым внутренним представлением «ключей», а затем для каждого «ключа» Гис создает способ создания соответствующего объекта.Также важно, чтобы дженерики в Java были реализованы с использованием стирания типов.Вот почему, когда вы пишете что-то вроде:

bind(classOf[SttpBackend[Try, Nothing]]).toProvider(classOf[SttpBackendProvider])

в raw-Guice, «ключ» фактически становится чем-то вроде «com.softwaremill.sttp.SttpBackend».К счастью, разработчики Guice подумали об этой проблеме с помощью дженериков и представили TypeLiteral[T], чтобы вы могли передавать информацию о дженериках.

Система типов Scala более обширна, чем в Java, и имеет некоторые лучшиеподдержка отражения от компилятора.Scala-Guice использует его для автоматического сопоставления типов Scala с этими более подробными ключами.К сожалению, он не всегда работает идеально.

Первая проблема - результат того факта, что тип SttpBackend определен как

trait SttpBackend[R[_], -S]

, поэтому он использует ожидаемый первый параметрбыть конструктором типов;и что первоначально Scala-Guice использовал инфраструктуру scala.reflect.Manifest.AFAIU, такие типы более высокого типа не могут быть представлены как Manifest, и именно об этом действительно говорит ошибка в вашем вопросе.

К счастью, Scala добавила новую инфраструктуру scala.reflect.runtime.universe.TypeTag для решения этой проблемы.проблема в лучшем и более последовательном виде, и Scala-Guice перешел на его использование.Вот почему в более новой версии Scala-Guice ошибка компилятора исчезает.К сожалению, в Scala-Guice есть еще одна ошибка, которая приводит к сбою кода во время выполнения, и это отсутствие обработки типа Nothing Scala.Видите ли, тип Nothing является своего рода подделкой в ​​JVM.Это одна из вещей, где система типов Scala более обширна, чем система Java.В мире JVM нет прямого сопоставления для Nothing.К счастью, нет способа создать какое-либо значение типа Nothing.К сожалению, вы все еще можете создать classOf[Nothing].Компилятор Scala-to-JVM обрабатывает его с помощью искусственного scala.runtime.Nothing$.Он не является частью общедоступного API, это детали реализации, в частности, Scala над JVM.В любом случае это означает, что тип Nothing нуждается в дополнительной обработке при преобразовании в Guice TypeLiteral, и его нет.Существует для Any двоюродного брата Nothing, но не для Nothing (см. Использование anyType в TypeConversions.scala ).

Так что на самом деле дваОбходные пути:

  1. Используйте сырой синтаксис на основе Java для Guice вместо красивого Scala-Guice:
bind(new TypeLiteral[SttpBackend[Try, Nothing]]() {})
    .toInstance(sttpBackend) // or to whatever

См. онлайн-демонстрация * на основе 1050 *на вашем примере.

Исправьте TypeConversions.scala в Scala-Guice, как в:
private[scalaguice] object TypeConversions {
  private val mirror = runtimeMirror(getClass.getClassLoader)
  private val anyType = typeOf[Any]
  private val nothingType = typeOf[Nothing] // added

  ...

  def scalaTypeToJavaType(scalaType: ScalaType): JavaType = {
    scalaType.dealias match {
      case `anyType` => classOf[java.lang.Object]
      case `nothingType` => classOf[scala.runtime.Nothing$] //added
      ...

Я попробовал это локально, и, похоже, это исправляет ваш пример.Я не проводил обширных тестов, так что это могло сломать что-то еще.

...