为了为我的新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。如何在不明确告知编译器假定OOut类型的情况下解决此问题?我的意思是,以下方法有效,但不会产生简洁的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

再次感谢。

最佳答案

通过将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()

10-06 07:33
查看更多