Scala类型推断限制

时间:2013-03-11 22:26:51

标签: scala types

我正在ReactiveMongo上开发一个ORM风格的库。目前我已经尝试实现嵌套的文档表示 - 但是我一直坚持使用Scala的类推理。我对Scala很新,所以欢迎每一个帮助。

这是我的尝试:

trait MongoDocument[T <: MongoDocument[T]] {
  self: T =>

  val db: DefaultDB
  val collection: String

  var fields: List[MongoField[T,_]] = List.empty
  def apply(doc: TraversableBSONDocument): T = { // loads the content of supplied document
    fields.foreach { field =>
      doc.get(field.field).foreach(field.load(_))
    }
    this
  }
}
trait MongoMetaDocument[T <: MongoDocument[T]] extends MongoDocument[T] {
  self: T =>

  type DocType = T

  protected val clazz = this.getClass.getSuperclass
  def create: T = clazz.newInstance().asInstanceOf[T]

  def find(id: String): Future[Option[T]] = {
    db(collection).find(BSONDocument("_id" -> BSONObjectID(id)))
      .headOption().map(a => a.map(create.apply(_)))
  }
}

abstract class MongoField[Doc <: MongoDocument[Doc], T](doc: MongoDocument[Doc], val field: String) {
  var value: Option[T] = None

  def is: Option[T] = value
  def apply(in: T) { value = Some(in) }
  def unset() { value = None }

  def load(bson: BSONValue)

  doc.fields ::= this
}

class MongoInteger[Doc <: MongoDocument[Doc]](doc: Doc, field: String)
  extends MongoField[Doc, Int](doc, field) {

  def load(bson: BSONValue) { value = Try(bson.asInstanceOf[BSONNumberLike].toInt).toOption }
}

class MongoDoc[Doc <: MongoDocument[Doc], SubDocF <: MongoMetaDocument[SubDoc], SubDoc <: MongoDocument[SubDoc]](doc: Doc, field: String, meta: SubDocF)
  extends MongoField[Doc, SubDocF#DocType](doc, field) {

  def load(bson: BSONValue) {
    value = Try(bson.asInstanceOf[TraversableBSONDocument]).toOption.map { doc =>
       meta.create.apply(doc)
    }
  }
}

假设我有以下代码:

class SubEntity extends MongoDocument[SubEntity] {
  val db = Db.get
  val collection = ""

  val field = new MongoInteger(this, "field")
}
object SubEntity extends SubEntity with MongoMetaDocument[SubEntity]

我想写另一个实体:

class Another extends MongoDocument[Another] {
  val db = Db.get
  val collection = "test"

  val subEntity = new MongoDoc(this, "subEntity", SubEntity)
}
object Another extends Another with MongoMetaDocument[Another]

Db.get只返回DefaultDB

然而,Scala无法推断MongoDoc实例的类型,即使我认为它们可能是可推断的(因为Doc很容易推断,SubDocF可以推断为SubEntity.typeSubEntity只是MongoMetaDocument[SubEntity]混合,SubDoc类型必须是SubEntity)。如果我使用以下代码,一切都很顺利:

val subEntity = new MongoDoc[Another,SubEntity.type,SubEntity](this, "subEntity", SubEntity)

是否有一些解决方案不需要明确设置类型?因为我需要构建一个从MongoDocument特征扩展的类的实例,所以我试图通过使用具有create方法的元对象来解决这个问题。

目前我提出的唯一解决方法是使用implicitly,但这会使实体定义更加讨厌。

感谢您帮助我解决这个问题(或者给我一些提示如何安排我的类层次结构,这不会有问题)

1 个答案:

答案 0 :(得分:2)

如果摆脱“F-bounded多态”,你会得到更好的类型推断。 (当你发现自己正在编写“T&lt;:Foo [T]”形式的类型参数时),而是在具体类中具体化的特征中使用抽象类型成员。

你会发现推理在这里起作用:

trait MongoDocument
  type DocType <: MongoDocument

  def apply(doc: TraversableBSONDocument): DocType = ??? // loads the content of supplied document
}
trait MongoMetaDocument extends MongoDocument {

  protected val clazz = this.getClass.getSuperclass
  def create: DocType = clazz.newInstance().asInstanceOf[DocType]

  def find(id: String): Future[Option[DocType]] = ???
}

abstract class MongoField[Doc <: MongoDocument, T](doc: MongoDocument, field: String) {
  type DocType <: MongoDocument
  var value: Option[T]

  def is: Option[T] = value
  def apply(in: T) { value = Some(in) }
  def unset() { value = None }

  def load(bson: BSONValue)

}

class MongoDoc[Doc <: MongoDocument, SubDocF <: MongoMetaDocument, SubDoc <: MongoDocument](doc: Doc, field: String, meta: SubDocF)
  extends MongoField[Doc, SubDocF#DocType](doc, field) {
  type DocType = Doc

  def load(bson: BSONValue) {
    value = Try(bson.asInstanceOf[TraversableBSONDocument]).toOption.map { doc =>
       ???
    }
  }
}

class SubEntity extends MongoDocument {
  type DocType = SubEntity
  ???
}
object SubEntity extends SubEntity with MongoMetaDocument

class Another extends MongoDocument {
  type DocType = Another
  val subEntity = new MongoDoc(this, "subEntity", SubEntity)
}
object Another extends Another with MongoMetaDocument