Компилятор Scala не может определить параметры типа - PullRequest
3 голосов
/ 13 августа 2011

Чтобы создать DSL для моего нового проекта Scala, я написал следующий код:

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

чтобы найти соответствующие документы для моих входных документов в MyDocDB, используя стратегию запросов MyQueryStrategy (определяется как значение по умолчанию для этого типа ввода и DocDB через неявное).

К сожалению, у компиляции 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)

Есть предложения?

Edit:

Большое спасибо за ваши ответы. Я наконец-то выбрал решение парадигмы, поскольку оно позволяет ориентировать стратегии запросов на конкретные 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 ]

3 голосов
/ 13 августа 2011

Я добился определенного успеха, превратив 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 голос
/ 13 августа 2011

Мне больше нравится подход парадигмы, но стоит упомянуть, что еще один прием, помогающий выводу типов, - это использование параметров более высокого типа. Например, 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, чтобы заставить ее компилироваться.

...