我正在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.type
而SubEntity
只是MongoMetaDocument[SubEntity]
混合,SubDoc
类型必须是SubEntity
)。如果我使用以下代码,一切都很顺利:
val subEntity = new MongoDoc[Another,SubEntity.type,SubEntity](this, "subEntity", SubEntity)
是否有一些解决方案不需要明确设置类型?因为我需要构建一个从MongoDocument
特征扩展的类的实例,所以我试图通过使用具有create
方法的元对象来解决这个问题。
目前我提出的唯一解决方法是使用implicitly
,但这会使实体定义更加讨厌。
感谢您帮助我解决这个问题(或者给我一些提示如何安排我的类层次结构,这不会有问题)
答案 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