【问题标题】:How to describe and fix this Scala type mismatch error?如何描述和修复这个 Scala 类型不匹配错误?
【发布时间】:2013-09-14 04:25:26
【问题描述】:

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

我正在编写一个将 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) 

为什么P 与T 不兼容,因为它们对T 没有约束?

【问题讨论】:

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


    【解决方案1】:

    这是存在类型(通配符类型的 Scala 等价物)是坏事 (TM) 的原因之一,最好避免在不执行 Java 互操作时:编译器不能(或不够聪明)推理通常关于哪些类型等于哪些类型,因为它们都消失了......

    为了让编译器理解这些类型是相同的,您需要以某种方式为该类型命名。

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

    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 的类型视图(反射和东西),尽管 它们比这三种结构更强大。 如果你可以设计一些东西来避免使用它们,你会省去很多痛苦。这有点争议,但至少,它们与类型推断交互的方式有很多限制,所以必须小心。

    【讨论】:

    • 我理解这里的情绪,但 Scala 的存在类型 仅 用于 Java 互操作性并不是真的(Scala 使用了一个完整的关键字——它没有所有这些)很多——例如forSome)。有很多完全合法的(虽然肯定是更高级的)用途与 Java 无关。
    • 是的,它们比 Java 的通配符等(而且更老)更强大,但我记得 Martin Odersky 在某处说过,如果不是为了与擦除兼容,它们就不会在 Scala 中,通配符和原始类型。
    • 我不理解存在类型——比如[T &lt;: Foo] 与[T forSome { type T &lt;: Foo }] 的含义——但我用另一个子问题更新了这个问题。我可能会在这里使用您的建议并定义辅助方法。谢谢。
    猜你喜欢
    • 2022-08-06
    • 2012-02-16
    • 1970-01-01
    • 1970-01-01
    • 2019-02-09
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 1970-01-01
    相关资源
    最近更新 更多