Почему scala не выводит правильный тип при переносе EitherT? - PullRequest
0 голосов
/ 27 января 2020

Вот код:

// eventually will be an implicit class with extension methods
class EitherTWrapper [L,R] (ei: EitherT[Future,L,R])

new EitherTWrapper(??? : EitherT[Future,Nothing,Boolean])

Не скомпилируется с:

type mismatch;
 found   : cats.data.EitherT[scala.concurrent.Future,Nothing,Boolean]
 required: cats.data.EitherT[scala.concurrent.Future,L,Boolean]
Note: Nothing <: L, but class EitherT is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)

Работает нормально, если я предоставляю типы явно, например:

new EitherTWrapper[Nothing,Boolean](??? : EitherT[Future,Nothing,Boolean])

Что сработало бы, за исключением того, что я не могу этого сделать, если я пытаюсь сделать его неявным классом.

Я ожидал, что это сработает. Как определить класс, который может обернуть EitherT?

Ответы [ 2 ]

0 голосов
/ 28 января 2020

По-видимому, это известная scala ошибка компилятора (ограничение?): https://github.com/scala/bug/issues/9453

Кажется, есть 2 обходных пути:

  • Сделать type covariant на оболочке (не очень хорошо, потому что теперь оболочка имеет другое поведение отклонения, чем оборачиваемая вещь, и работает только в том случае, если параметр типа не используется контравариантно).
  • Обработайте версию EitherT с помощью Nothing специально, создав для нее отдельную оболочку. Это работает даже при попытке использовать оболочки как неявные классы.
0 голосов
/ 28 января 2020

Как показывает ошибка, изменение оболочки на class EitherTWrapper [+L,R] (ei: EitherT[Future,L,R]) исправит ошибки компиляции.

В вашей ошибке указано, что Nothing <: L, but class EitherT is invariant in type A. - это означает, что Nothing является подтипом L, поэтому объявление L недопустимо, поскольку подразумевает, что вы явно хотите L, а не его подтипы (т. е. L является инвариантом).

Объявление чего-либо как +L делает его ковариантным, делая то, что вы хотите, возможным. Узнайте больше о дисперсии в scala документах: https://docs.scala-lang.org/tour/variances.html

Причина, по которой работает приведенный ниже код (из документов), заключается в том, что Scala '* List определен как List[+A], что означает, что вы также можете передать List[Cat] и List[Dog] в функцию, которая принимает List[Animal]:

abstract class Animal {
  def name: String
}
case class Cat(name: String) extends Animal
case class Dog(name: String) extends Animal

object CovarianceTest extends App {
  def printAnimalNames(animals: List[Animal]): Unit = {
    animals.foreach { animal =>
      println(animal.name)
    }
  }

  val cats: List[Cat] = List(Cat("Whiskers"), Cat("Tom"))
  val dogs: List[Dog] = List(Dog("Fido"), Dog("Rex"))

  printAnimalNames(cats)
  // Whiskers
  // Tom

  printAnimalNames(dogs)
  // Fido
  // Rex
}

On Scast ie

...