处理具有不变类型参数

时间:2017-08-25 12:23:28

标签: scala match type-parameter

我认为,我的问题最好通过一些示例代码来描述:

 class Foo[T] 
 class Bar extends Foo[String] 
 class Baz extends Foo[Int] 


 trait X { def f: Foo[_] }
 case class Wrapper[D](f: Foo[D]) extends X
 val w: X = Wrapper(new Bar)
 w match { case Wrapper(_: Bar) => 1 }

最后一行以

失败
  found   : Bar
  required: Foo[Any]
  Note: String <: Any (and Bar <: Foo[String]), but class Foo is invariant in type T.
  You may wish to define T as +T instead. (SLS 4.5)

我理解这是因为unapply是使用类型参数定义的,该参数被推断为Any,因此它抱怨String不兼容。

但问题是,是否有任何方法可以使其发挥作用?我尝试给exctractor一个类型参数,如:w match { case Wrapper[String](_: Bar) => 1 },但是它说它不带参数(这是谎言)...... :(

到目前为止,我想出的唯一方法是这个丑陋的宝贝:

 w match { case w: Wrapper[String] if w.f.isInstanceOf[Bar]  => 1 }

或者,也许,

Option(w).map(_.f) match { case Some(_: Bar) => 1 }

(后者有效,因为Option是协变的,但不幸的是我不能使我的类协变)。另外,如果没有额外的丑陋IRL,我真的不能使用最后一种选择,因为在现实生活中相当于X实际上并没有f

有更好的想法吗?

2 个答案:

答案 0 :(得分:2)

定义自定义提取器

Wrapper.unapply确实采用了type参数,但是你不能在模式匹配序列中指定一个*,因此编译器会为你推断一个*(如果编译器这样做,那么它常常是AnyNothing)。

实际上,您并不想要它,因为当您强迫元素键入X时,您将删除类型信息。所以,你想要一个存在版本的匹配器

object WrapperEx {
  def unapply(w: Wrapper[_]): Option[Foo[_]] = Wrapper.unapply(w)
}

并像这样使用它:

w match { case WrapperEx(_: Bar) => 1 }

Runnable version here

  • 好消息:您可以委托生成的案例类匹配器。
  • 坏消息:你无法在案例类伴侣中定义它。斯卡拉很乐意选错了,所以它无法消除歧义。

尽管如此,我还是说它不是半坏的

*你可以使用最新的Typelevel Scala,但我不确定它是如何用于类型转换的,我无法让它适用于你的情况。

答案 1 :(得分:1)

您可以参数化trait X以摆脱def f: Foo[_]中的存在类型,我认为这是编译器的绊倒。以下代码有效:

class Foo[T]
class Bar extends Foo[String]
class Baz extends Foo[Int]

trait X[A] { def f: Foo[A] }
case class Wrapper[D](f: Foo[D]) extends X[D]
val w: X[String] = Wrapper(new Bar) // type ascription can be omitted and will be inferred
w match { case Wrapper(_: Bar) => 1 }