首页 文章

如何在不使用依赖类型的情况下进行模式匹配?

提问于
浏览
0

这很难说,所以请让我举个例子:

trait Cache

trait QueryLike {
  type Result
}

trait Query[A] extends QueryLike {
  type Result = A
  def exec: Result
}

trait CachedQuery[A] extends QueryLike {
  type Result = A
  def execWithCache(cache: Cache): Result
}

def exec(query: QueryLike)(implicit cache: Cache): query.Result = query match {
  case q: Query[query.Result] => q.exec
  case cq: CachedQuery[query.Result] => cq.execWithCache(cache)
}

这种编译和运行正常,因为模式匹配是在不同类型( QueryCachedQuery )上完成的,而不是依赖于像this question这样的泛型 .

但我仍然得到编译器警告:

警告:(18,12)抽象类型类型模式中的结果A $ A4.this.Query [query.Result]未被选中,因为它被擦除情况q消除了:查询[query.Result] => q.exec

因为我无论如何都不直接使用依赖类型 query.Result (比如将它转换为不同的操作),所以'd be ideal to just erase it completely and do away with the warning. But unfortunately, using wildcard doesn' t的工作原因很充分:

...
case q: Query[_] => q.exec // type mismatch
case cq: CachedQuery[_] => cq.execWithCache(cache)
...

有没有更好的方法来做到这一点而不产生编译器警告?

2 回答

  • 2

    此错误并非特定于路径相关类型 . 如果您尝试匹配任何 Query[A] ,您将得到相同的错误,因为类型参数 A 在运行时被擦除 . 在这种情况下,它正在寻找's not possible that the type parameter can be anything other than the type you' . 由于 Query[A]QueryLike { type Result = A} ,它也应该是 Query[query.Result] ,尽管这是一种看起来有点不同寻常的方式 . 如果您愿意,可以使用 @unchecked 注释来禁止警告:

    def exec(query: QueryLike)(implicit cache: Cache): query.Result = query match {
      case q: Query[query.Result @unchecked] => q.exec
      case cq: CachedQuery[query.Result @unchecked] => cq.execWithCache(cache)
    }
    

    虽然很难说这是否适用于您的实际用例,但您也可以重新构建代码以避免完全匹配,并通过多态性更优雅地处理它(可能) . 由于最后一次 exec 无论如何都需要一个隐含的 Cache ,所以对于每一个 QueryLike 来说这似乎都不会受到伤害 . 您的API可以通过这种方式更加统一,并且您无需确定要调用的方法 .

    trait Cache
    
    trait QueryLike {
      type Result
      def exec(implicit cache: Cache): Result
    }
    
    trait Query[A] extends QueryLike {
      type Result = A
    }
    
    trait CachedQuery[A] extends QueryLike {
      type Result = A
    }
    
    def exec(query: QueryLike)(implicit cache: Cache): query.Result = query.exec
    

    如果 Query[A] 需要没有 Cacheexec ,您还可以提供带有 DummyImplicit 的重载,以允许它在没有 DummyImplicit 的情况下工作 .

    def exec(implicit d: DummyImplicit): Result
    
  • 0

    实际上,问题似乎是路径依赖类型非常具体:问题是q和cq的类型 . q.Result是一个不兼容的查询类型 . 结果因为Scala类型检查器不知道,该查询和q和qc必须是相同的引用 .

    因此,Query [query.Result]实际上确实需要运行时类型检查 . 我注意到了这一点,当我尝试删除类型参数并只使用内部结果时 . 然后模式匹配不再生成警告,但q.exec的返回类型将不与query.Result兼容 .

    不使用@unchecked或asInstanceOf等的一种解决方案是将Result转换为QueryLike的类型参数 . 一般情况下,您不应该将抽象类型成员用于您想要“在上下文中”自由传递的内容 . 因此,在继承层次结构中的某个位置将类型成员转换为类型参数有点奇怪 .

    所以这很好,正如编译器所知,他不必检查类型参数:

    trait QueryLike[A] {
    
    }
    
    trait Query[A] extends QueryLike[A] {
      def exec: A
    }
    
    trait CachedQuery[A] extends QueryLike[A] {
      def execWithCache(cache: Cache): A
    }
    
    def exec[A](query: QueryLike[A])(implicit cache: Cache): A = query match {
      case q: Query[A] => q.exec
      case cq: CachedQuery[A] => cq.execWithCache(cache)
    }
    

    另一种方法是将方法添加到CachedQuery和Query的公共基本特征 .

    trait QueryLike {
      type Result
    }
    
    trait Query[A] extends QueryLike with ExecWithCache {
      type Result = A
      def exec: Result
      override def execWithCache(implicit cache: Cache) = exec
    }
    
    trait CachedQuery[A] extends QueryLike with ExecWithCache {
      type Result = A
      def exec(cache: Cache): Result
      override def execWithCache(implicit cache: Cache) = exec(cache)
    }
    
    trait ExecWithCache extends QueryLike {
      def execWithCache(implicit cache: Cache): Result
    }
    

    要改变这一点,Scala可能必须能够确定两个稳定访问器何时相同 .

相关问题