Update: I modified the example so that can be compiled and tested.
I have an implicit class that defines an enrichment method:
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;
}
The problem is it doesn't work when R is Nothing in the second example. It results in an compiler error: In such a case, I get the following compiler error:
Pipe.scala:19: error: type mismatch; found : Pipe[Int,Int,R] required: Pipe[Int,Int,String] val q2: Pipe[Int,Int,String] = p2 >-> p2;
Why does this happen?
I managed to solve it by creating a separate implicit class for that case:
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];
But can I rely on Scala's behavior in the future, that it will not consider Nothing as an option for R? If that changes in the future, the code will stop working because I'll have two different applicable implicits.
Well... You haven't shown all your code, and the code you did show has some confusing inconsistencies. So this is going to be a wild guess. I suspect your problem is that
Pipeis invariant in its type parameterR. Here's my simplified example:The compile error I get from this code is:
... which I admit is not the same as the error you posted. But I don't entirely trust what you posted, so I'm going to keep going! The fix for this is to make
Testcovariant in its type parameterA:I honestly don't understand why the compile error happens to begin with. It seems like the compiler doesn't want to unify
A =:= Nothingin the conversion toTestOps, but I don't see why not. Nevertheless,Testshould be covariant inAanyways, and I'm guessing yourPipeclass should likewise be covariant inR.Edit
I just spent a few minutes looking through the Scala bug list and found several possibly related issues: SI-1570, SI-4509, SI-4982 and SI-5505. I don't really know any details, but it sounds like
Nothingis treated specially and shouldn't be. Paul and Adriaan would be the guys to ask...