2013-03-09 9 views
3

Обновление: Я изменил пример, чтобы его можно было скомпилировать и протестировать.Почему неявный класс 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]; 

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

+0

Не должен 'Pipe.fuse (это, это)' быть скорее 'Pipe.fuse (pipe, that)'? – ghik

+0

И, кстати, что такое 'B'? Кажется, он нигде не объявлен. – ghik

+0

Не могли бы вы показать подпись 'Pipe.fuse'? – ghik

ответ

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 bug list и нашел несколько, возможно, проблем, связанных с: SI-1570, SI-4509, SI-4982 и SI-5505. Я действительно не знаю никаких подробностей, но звучит так, как Nothing обрабатывается специально и не должно быть. Paul и Adriaan были бы ребятами, чтобы спросить ...

+1

Определяется как 'trait Pipe [-I, + O, + R]'. Я буду обновлять код завтра, чтобы он был полным и может быть скомпилирован. –

+0

Я просто понял, что это все в вашем репапоре 'scala-conduit'. Я просто собираюсь клонировать это и видеть, что я могу понять ... – mergeconflict

+0

Просто отправил вам запрос на тягу :) – mergeconflict