【问题标题】:Scala compiler fails to infer type parametersScala 编译器无法推断类型参数
【发布时间】:2011-08-13 14:13:50
【问题描述】:

为了为我的新 Scala 项目创建 DSL,我编写了以下代码:

trait DocDB[O] {
    def searchFor[I] (docs: Iterable[I], queryStrategy: QueryStrategy[I, DocDB[_]]): Iterable[(I,O)]
}
trait QueryStrategy[I, +F <: DocDB[_]]

class In  // class of input documents
class Out // class of output documents
// MyDocDB
object MyDocDB extends DocDB[Out] {
    def searchFor[I] (docs: Iterable[I], queryStrategy: QueryStrategy[I, DocDB[_]]) = List()
}
// MyQueryStrategy for In and MyDocDB
implicit object MyQueryStrategy extends QueryStrategy[In, MyDocDB.type]

implicit def listExt[I] (items: Iterable[I]) = new {
    def findAt[O, F <: DocDB[O]](docDB: F) = new {
        def apply(implicit queryStrategy: QueryStrategy[I, F]): Iterable[(I,O)] = {
            docDB.searchFor[I](items, queryStrategy)
        }
    }
}

我真正想要的是能够写作

val out1: Iterable[(In, Out)] = List[In]() findAt MyDocDB

通过使用查询策略 MyQueryStrategy(为此输入类型和 DocDB 通过隐式定义的默认值)在 MyDocDB 中查找我的输入文档的相应文档。

不幸的是,Scala 编译时该行存在问题。它声称它无法推断类型:

error: inferred type arguments [Nothing,test.StackOverflow.MyDocDB.type] do not conform to method findAt's type parameter bounds [O,F <: test.StackOverflow.DocDB[O]]
val out1: Iterable[(In, Out)] = List[In]() findAt MyDocDB

不知何故,它推断出Nothing 而不是Out。如果不明确告诉编译器它应该假定O 的类型为Out,我该如何解决这个问题?我的意思是,以下内容有效,但不会导致简洁的 DSL:

val out2: Iterable[(In, Out)] = List[In]().findAt[Out, MyDocDB.type](MyDocDB).apply(MyQueryStrategy)

有什么建议吗?

编辑:

非常感谢您的回答。我终于采用了范式的解决方案,因为它允许查询策略可以针对我在项目中实际需要的特定 DocDB。除了范例的解决方案,我将 listExt 函数替换为

implicit def listExt[I] (items: Iterable[I]) = new {
    def findAt[F <: DocDB](docDB: F)(implicit queryStrategy: QueryStrategy[I, F]): Iterable[(I,F#O)] = {
        docDB.searchFor[I](items, queryStrategy)
    }
}

这样我就可以省略 apply 方法和隐式 QueryStrategy:

val out1: Iterable [(In,Out)] = List[In]() findAt MyDocDB

再次感谢。

【问题讨论】:

    标签: scala dsl type-inference type-systems


    【解决方案1】:

    通过将O 转换为DocDB 中的抽象类型并使用类型投影,我取得了一些成功:

    trait DocDB {
      type O
      def searchFor[I](
        docs: Iterable[I],
        queryStrategy: QueryStrategy[I, DocDB]
      ): Iterable[(I,O)]
    }
    
    trait QueryStrategy[I, +F <: DocDB]
    
    class In  // class of input documents
    class Out // class of output documents
    
    object MyDocDB extends DocDB {
    
      type O = Out
      def searchFor[I](
        docs: Iterable[I], 
        queryStrategy: QueryStrategy[I, DocDB]
      ) = List()
    }
    
    implicit object MyQueryStrategy extends QueryStrategy[In, MyDocDB.type]
    
    implicit def listExt[I] (items: Iterable[I]) = new {
      def findAt[F <: DocDB](docDB: F) = new {
        def apply(implicit queryStrategy: QueryStrategy[I, F]): 
        Iterable[(I,F#O)] = {
          docDB.searchFor[I](items, queryStrategy)
        }
      }
    }
    

    它似乎有效:

    scala> val out1: Iterable [(In,Out)] = List[In]() findAt MyDocDB apply
    out1: Iterable[(In, Out)] = List()
    

    【讨论】:

    • 非常感谢。奇迹般有效。 :)
    【解决方案2】:

    我最喜欢范式的方法,但值得一提的是,帮助类型推断的另一个技巧是使用更高种类的类型参数。比如QueryStrategy中的F得到了自己的类型参数,变成了F[o]。这是我可以编译的内容:

    trait DocDB[O] {
        def searchFor[I] (docs: Iterable[I], queryStrategy: QueryStrategy[I, O, DocDB]): Iterable[(I,O)]
    }
    trait QueryStrategy[I, O, +F[o] <: DocDB[o]]
    
    class In
    class Out
    
    object MyDocDB extends DocDB[Out] {
      def searchFor[I] (docs: Iterable[I], queryStrategy: QueryStrategy[I, Out, DocDB]) = List()
    }
    
    object Foo {
      implicit object MyQueryStrategy extends QueryStrategy[In, Out, DocDB] // !! won't work QueryStrategy[In, Out, MyDocDB.type] !!
    
      trait Finder[I] {
        def findAt[O, F[o] <: DocDB[o]](docDB: F[O])(implicit queryStrategy: QueryStrategy[I, O, F]): Iterable[(I, O)]
      }
    
      implicit def listExt[I] (items: Iterable[I]) = new Finder[I] {
        def findAt[O, F[o] <: DocDB[o]](docDB: F[O])(implicit queryStrategy: QueryStrategy[I, O, F]): Iterable[(I, O)] = {
          docDB.searchFor[I](items, queryStrategy)
        }
      }
    
      val out1: Iterable[(In, Out)] = List[In]() findAt(MyDocDB)
    }
    

    一个限制是,在对象MyQueryStrategy 中,不能使用特定类型参数MyDocDB.type 实例化QueryStrategy,因为它的类型错误。我还遇到了一些与您使用结构类型定义 findAt 并在其中定义 apply 相关的问题。根据我的经验,结构类型往往会遇到各种困难,因此我引入了一个显式的Finder trait 来使其编译。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多