当类型参数之一应该是 Nothing 时,为什么 Scala 的隐式类不起作用? [英] Why doesn't Scala's implicit class work when one of the type parameters should be Nothing?

查看:54
本文介绍了当类型参数之一应该是 Nothing 时,为什么 Scala 的隐式类不起作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:我修改了示例,以便可以编译和测试.

我有一个定义丰富方法的隐式类:

case class Pipe[-I,+O,+R](f: I => (O, R));对象管道{//有问题的隐式类:隐式类 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;//有效的例子:val p1: Pipe[Int,Int,String] = Pipe((x: Int) => (x, ""));val q1: Pipe[Int,Int,String] = p1 >->p1;//没有的例子,只是因为 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: 错误:类型不匹配;发现:管道[Int,Int,R]要求:管道[Int,Int,String]val q2: Pipe[Int,Int,String] = p2 >->p2;

为什么会这样?

<小时>

我设法通过为这种情况创建一个单独的隐式类来解决它:

trait Fuse[I,O,R] extends Any {def >->[X](即:Pipe[O,X,R])(隐式终结器:Finalizer):Pipe[I,X,R];}受保护的特性 FuseImpl[I,O,R] 扩展 Any 与 Fuse[I,O,R] {定义管道:管道[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);}隐式类 PipeEnrich[I,O,R](val pipe: Pipe[I,O,R])使用 FuseImpl[I,O,R] 扩展 AnyVal;隐式类 PipeEnrichNothing[I,O](val pipe: Pipe[I,O,Nothing])使用 FuseImpl[I,O,Nothing] 扩展 AnyVal;

但是我可以依赖 Scala 在未来的行为,它不会将 Nothing 视为 R 的选项吗?如果将来发生变化,代码将停止工作,因为我将有两个不同的适用隐式.

解决方案

嗯... 您还没有展示所有代码,而且您展示的代码有一些令人困惑的不一致之处.所以这将是一个疯狂的猜测.我怀疑您的问题是 Pipe 在其类型参数 R 中是不变的.这是我的简化示例:

case class Test[A](a: A)对象测试{隐式类 TestOps[A](val lhs: Test[A]) extends AnyVal {def >->(rhs: Test[A]): Test[A] = ???}定义测试{def lhs = 测试(???)def rhs = 测试(???)lhs >->rhs}}

我从这段代码中得到的编译错误是:

value >->不是 Test[Nothing] 的成员lhs >->rhs^

... 我承认这与您发布的错误不同.但我并不完全相信你发布的内容,所以我会继续前进!对此的解决方法是使 Test 在其类型参数 A 中具有协变:

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

老实说,我不明白为什么会出现编译错误.似乎编译器不想在转换为 TestOps 时统一 A =:= Nothing,但我不明白为什么不这样做.尽管如此,Test 无论如何应该在 A 中协变,我猜你的 Pipe 类在 R 中也应该是协变的代码>.

编辑

我花了几分钟浏览了 Scala 错误列表,发现了几个可能相关的问题:SI-1570SI-4509SI-4982SI-5505.我真的不知道任何细节,但听起来 Nothing 被特殊对待,不应该被特殊对待.Paul 和 Adriaan 会是要问的人...

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 Pipe is invariant in its type parameter R. Here's my simplified example:

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
  }
}

The compile error I get from this code is:

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

... 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 Test covariant in its type parameter A:

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

I honestly don't understand why the compile error happens to begin with. It seems like the compiler doesn't want to unify A =:= Nothing in the conversion to TestOps, but I don't see why not. Nevertheless, Test should be covariant in A anyways, and I'm guessing your Pipe class should likewise be covariant in R.

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 Nothing is treated specially and shouldn't be. Paul and Adriaan would be the guys to ask...

这篇关于当类型参数之一应该是 Nothing 时,为什么 Scala 的隐式类不起作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆