为了为我的新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
再次感谢。
答案 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
特性来使其编译。