3

我正在 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.typeSubEntitymixins,MongoMetaDocument[SubEntity]然后SubDoc类型必须是SubEntity)。如果我使用以下代码,一切都会很好:

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

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

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

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

4

1 回答 1

2

如果您摆脱“F-bounded Polymorphism”,您将获得更好的类型推断。(即当您发现自己正在编写“T <: 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
于 2013-03-12T03:58:12.277 回答