Почему неявный класс Scala не работает, когда один из параметров типа должен быть Nothing?

Обновление: я изменил пример, чтобы его можно было скомпилировать и протестировать.

У меня есть неявный класс, который определяет метод обогащения:

case class Pipe[-I,+O,+R](f: I => (O, R));

object Pipe {
  // The problematic implicit class:
  implicit class PipeEnrich[I,O,R](val pipe: Pipe[I,O,R]) extends AnyVal {
    def >->[X](that: Pipe[O,X,R]): Pipe[I,X,R] = Pipe.fuse(pipe, that);
    def <-<[X](that: Pipe[X,I,R]): Pipe[X,O,R] = Pipe.fuse(that, pipe);
  }

  def fuse[I,O,X,R](i: Pipe[I,O,R], o: Pipe[O,X,R]): Pipe[I,X,R] = null;

  // Example that works:
  val p1: Pipe[Int,Int,String] = Pipe((x: Int) => (x, ""));
  val q1: Pipe[Int,Int,String] = p1 >-> p1;

  // Example that does not, just because R = Nothing:
  val p2: Pipe[Int,Int,Nothing] = Pipe((x: Int) => (x, throw new Exception));
  val q2: Pipe[Int,Int,String] = p2 >-> p2;
}

Проблема в том, что это не работает, когда R является Nothing во втором примере. Это приводит к ошибке компилятора: в таком случае я получаю следующую ошибку компилятора:

Pipe.scala:19: error: type mismatch;
 found   : Pipe[Int,Int,R]
 required: Pipe[Int,Int,String]
  val q2: Pipe[Int,Int,String] = p2 >-> p2;

Почему это происходит?


Мне удалось решить эту проблему, создав отдельный неявный класс для этого случая:

trait Fuse[I,O,R] extends Any {
  def >->[X](that: Pipe[O,X,R])(implicit finalizer: Finalizer): Pipe[I,X,R];
}

protected trait FuseImpl[I,O,R] extends Any with Fuse[I,O,R] {
  def pipe: Pipe[I,O,R];
  def >->[X](that: Pipe[O,X,R]) = Pipe.fuse(pipe, that);
  def <-<[X](that: Pipe[X,I,R]) = Pipe.fuse(that, pipe);
}

implicit class PipeEnrich[I,O,R](val pipe: Pipe[I,O,R])
  extends AnyVal with FuseImpl[I,O,R];
implicit class PipeEnrichNothing[I,O](val pipe: Pipe[I,O,Nothing])
  extends AnyVal with FuseImpl[I,O,Nothing];

Но могу ли я рассчитывать на поведение Скалы в будущем, которое не будет учитывать Nothing как вариант для R? Если это изменится в будущем, код перестанет работать, потому что у меня будет два различных применимых следствия.

1 ответ

Решение

Хорошо... Вы не показали весь свой код, и код, который вы показали, имеет некоторые запутанные несоответствия. Так что это будет дикое предположение. Я подозреваю, что ваша проблема в том, что Pipe инвариантен по своему параметру типа R, Вот мой упрощенный пример:

case class Test[A](a: A)

object Test {
  implicit class TestOps[A](val lhs: Test[A]) extends AnyVal {
    def >->(rhs: Test[A]): Test[A] = ???
  }

  def test {
    def lhs = Test(???)
    def rhs = Test(???)
    lhs >-> rhs
  }
}

Ошибка компиляции, которую я получаю из этого кода:

value >-> is not a member of Test[Nothing]
     lhs >-> rhs
         ^

... что я признаю, не то же самое, что ошибка, которую вы опубликовали Но я не совсем доверяю тому, что вы написали, поэтому я буду продолжать! Исправление для этого состоит в том, чтобы сделать Test ковариантный по своему параметру типа A:

case class Test[+A](a: A)

Я, честно говоря, не понимаю, почему ошибка компиляции происходит с самого начала. Кажется, компилятор не хочет объединяться A =:= Nothing в преобразовании в TestOpsНо я не понимаю, почему нет. тем не менее, Test должен быть ковариантным в A в любом случае, и я угадываю ваш Pipe класс также должен быть ковариантным в R,

редактировать

Я просто потратил несколько минут на просмотр списка ошибок Scala и обнаружил несколько возможных проблем: SI-1570, SI-4509, SI-4982 и SI-5505. Я действительно не знаю никаких деталей, но это звучит как Nothing лечится специально и не должно быть. Пол и Адриан будут парнями, чтобы спросить...

Другие вопросы по тегам