Scala编译器无法推断类型参数

时间:2011-08-13 14:13:50

标签: scala dsl type-inference type-systems

为了为我的新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

再次感谢。

2 个答案:

答案 0 :(得分:3)

我在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()

答案 1 :(得分:1)

我最喜欢范式的方法,但值得一提的是,辅助类型推断的另一个技巧是使用更高的kinded类型参数。例如,F中的QueryStrategy获取自己的类型参数,并变为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中,无法使用特定类型参数QueryStrategy实例化MyDocDB.type,因为它的类型错误。我也遇到了一些与使用结构类型相关的问题,以定义findAt及其apply。根据我的经验,结构类型往往会遇到各种各样的困难,因此我引入了一个明确的Finder特性来使其编译。