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

Kev*_*vin 7 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
Run Code Online (Sandbox Code Playgroud)

和两个功能:

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)
Run Code Online (Sandbox Code Playgroud)

现在,我可以执行某种形式的动态调度和调用,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
    }
  }
Run Code Online (Sandbox Code Playgroud)

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

case o @ Sub2(a, f) => bar(a, f)(o.ev) // type mismatch
Run Code Online (Sandbox Code Playgroud)

可行f: B => B(我可以打电话foo),但是f: B => Unit不起作用(我不能打电话),这很奇怪bar

Ale*_*nov 3

您实际上可以使用类型变量模式使其工作:

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)
    }
  }
Run Code Online (Sandbox Code Playgroud)

这部分是对评论的回答,因为它并不真正适合那里:

顺便说一句,还有一个微妙之处我不明白:为什么 B => B 中的单位逆变

编译器的逻辑是什么 Nothing => Unit inference Staff

您需要从函数方差开始。X => Y是 的子类型X1 => Y1当且仅当X是 的超类型X1并且Y是 的子类型Y1。我们说它在 中是逆变的X,在 中是协变的Y

因此,如果你修复Y = Unit,剩下的只是 中的逆变XAny => Unit是 的子类型String => Unit, 是 的子类型Nothing => Unit。事实上,Nothing => Unit是最一般的B => Unit,这就是为什么它会在这个Sub2案例中被推断出来。

并且 B => B 不是(因为它推断 Any => Any)?

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