对包含广义类型约束的类进行模式匹配

时间:2019-04-03 12:35:18

标签: scala

我的特征可以被多个子类扩展

trait Sup
case class Sub[A, B](a: A, f: B => B)(implicit val ev: A =:= B) extends Sup
case class Sub2[A, B](a: A, f: B => Unit)(implicit val ev: A =:= B) extends Sup

和两个功能:

def foo[A, B](a: A, f: B => B)(implicit ev: A =:= B) = f(a)
def bar[A, B](a: A, f: B => Unit)(implicit ev: A =:= B) = f(a)

现在,我可以执行某种形式的动态调度,如果对象是foo,则调用Sub,如果对象是bar,则调用Sub2

def dispatch(obj: Sup) = {
    obj match {
      case Sub(a, f) => foo(a, f)
      case Sub2(a, f) => bar(a, f) // type mismatch: found: Nothing => Unit. required: B => Unit
    }
  }

我也尝试过明确地传递证据,但是会导致相同的错误:

case o @ Sub2(a, f) => bar(a, f)(o.ev) // type mismatch

f: B => B有效(我可以叫foo),但是f: B => Unit不起作用(我不能叫bar)很奇怪。

2 个答案:

答案 0 :(得分:3)

没有回答,但要考虑一下:

case class Sub1[A, B](a: A, f: B => B)
case class Sub2[A, B](a: A, f: B => Unit)

def foo[A, B](a: A, f: B => B)(implicit ev: A =:= B) = f(a)
def bar[A, B](a: A, f: B => Unit)(implicit ev: A =:= B) = f(a)

def dispatch(obj: Any) = obj match {
    case Sub1(a, f) => foo(a, f)
    case Sub2(a, f) => bar(a, f) // type mismatch: found: Nothing => Unit. required: B => Unit
}

此代码与您的代码有相同的问题,但是Sub1Sub2案例类甚至没有implicit块。

据我了解,万一类的implicit部分不会影响模式解析。本节仅是在apply(a: A, f: B => B)(implicit val ev: A =:= B)的伴随对象上调用Sub1/2方法的语法糖。模式匹配使用unapply方法在运行时匹配模式,而这个unapply甚至不知道证据。

但是我仍然想知道为什么在没有此证据的情况下首先编译case

编辑::添加来自@AlexeyRomanov的有用评论

  

类型推断比类型擦除要多。但是,是的,编译器推断a的类型为Any和f的Any => Any,然后生成并使用证据表明Any =:= Any。在第二种情况下,它为f推断Nothing => Unit,因为B => Unit在B中是反变量的,并且找不到Any =:= Nothing。

答案 1 :(得分:2)

您实际上可以使用type variable patterns使其起作用:

def dispatch(obj: Sup) = {
    obj match {
      case obj: Sub[a, b] => foo(obj.a, obj.f)(obj.ev)
      case obj: Sub2[a, b] => bar(obj.a, obj.f)(obj.ev)
    }
  }

这部分是对评论的答案,因为它实际上并不适合其中:

  

顺便说一句,我仍然没有得到一个微妙之处:为什么B => B中的单位不变

     

此Nothing =>单元推理人员的编译器逻辑是什么

您需要从function variance开始。当且仅当X => YX1 => Y1的超类型且XX1的子类型时,YY1的子类型。我们说X是协变的,而Y是协变的。

因此,如果您修复了Y = Unit,剩下的只是X中的变量。 Any => UnitString => Unit的子类型,它是Nothing => Unit的子类型。实际上,Nothing => Unit是所有B => Unit中最通用的,这就是为什么在Sub2情况下可以推断出它的原因。

  

和B => B不是(因为它推断出Any => Any)?

B => B的情况有所不同:String => String既不是Any => Any也不是Nothing => Nothing的子类型或超类型。也就是说,B => B是不变的。因此,没有原则上的理由来推断任何特定的B,在这种情况下,编译器使用BAny)的上限,并且B => B变成{{1 }}。