如何描述和修复此Scala类型不匹配错误?

时间:2013-09-13 17:10:25

标签: scala type-systems type-mismatch existential-type parameterized-types

以下是我有时会遇到参数化类型的一种情况的具体实例。基本上,我知道类型参数是兼容的,但我不知道如何证明代码的某些部分。

我正在编写一个将url映射到处理函数的请求路由器。下面是一些简化的代码。我创建了List[Route] Route基本上是UrlMatcher, Function对。

class Route[A](matcher: UrlMatcher[A], handler: HandlerFunction[A])

abstract class UrlMatcher[A] {
   def match(url: String): Option[A]   // None if no match

类型参数A用于匹配器可能从URL中提取的“参数”。它们将被传递给处理函数。例如,看到像“/ users / 123”这样的URL路径的UrlMatcher[Int]可以将123传递给getUser(id: Int)函数。路由器可能如下所示:

val routes = ArrayBuffer[Route[_]]

def callHandler(url: String) {
  for (r <- routes) {
    val args = r.matcher.matchUrl(url)
    if (args.isDefined)
      r.handler(args.get)  // <--- error here
    }

问题是我遇到类型不匹配错误,因为我不知道如何告诉它两种类型是相同的。

type mismatch; found: args.type (with underlying type Option[Any])  
            required: _$1  

我知道我可以重新设计它,以便Route有一个类似matchAndCall的方法,但我想尽可能保持这种逻辑流程。

更新/修改

我不完全理解存在类型,但我试过这个......

val routes = ArrayBuffer[T forSome { type T }]()

它消除了上面的不匹配错误。但是,我在另一个插入ArrayBuffer

的地方
def route[P](matcher: UrlMatcher[P], handler: Handler[P]): AbstractRoute = {
  val route = new Route(matcher, handler)
  otherRoutes.append(route)   // error here  
  route
}

现在错误是......

type mismatch;  found : Route[P]  required: Route[T forSome { type T }] Note: P <: T
forSome { type T }, but class Route is invariant in type P. You may wish to define 
P as +P instead. (SLS 4.5) 

为什么PT不兼容,因为它们对T没有限制?

1 个答案:

答案 0 :(得分:3)

这是存在类型(Scala等效的通配符类型)是Bad Thing(TM)的原因之一,最好在不进行Java互操作时避免使用:编译器不能(或者只是不够智能)推理通常关于哪些类型与哪些类型相同,因为它们都已消失......

要使编译器理解这些类型是相同的,您需要以某种方式为该类型指定名称。

类型参数是一种可能性:您可以使用for comprehension的内容定义参数化方法,以便在方法中,类型是众所周知的。

def callHandler(url: String) {
  def call[T](r: Route[T]) = {
    val args = r.matcher.matchUrl(url)
    if (args.isDefined) r.handler(args.get)
    // or args.foreach(r.handler)
  }
  for (r <- routes) call(r)
  // or routes.foreach(call)
}

注意:在更简单的情况下,您也可以使用方差来获得List[Route[Any]],并且您的问题就会消失,类型再次众所周知。在这里,我不确定你是否可以Route[A]协变。

Existential types主要用于表示Java通配符,Java原始类型和JVM的类型视图(反射和填充),即使它们比这三种结构更强大如果你可以设计一些东西以避免使用它们,你将会为自己省去很多痛苦。这有点争议,但至少,它们与类型推断的交互方式有很多局限性所以一定要小心。