后台:我正在使用net.liftweb.record
与MongoDB访问数据库。在某些时候,我需要从数据库中绘制一个文档集合表(并将它们呈现为ASCII表)。我遇到了非常模糊的类型推断问题,这些问题很容易解决,但仍然让我想知道它们为什么会发生。
复制:为简单起见,我将代码减少到(我认为是)绝对最小值,因此它只取决于net.liftweb.record
而没有任何Mongo特定类型。我保留了所讨论功能的现实主体,使这个例子更加真实。
makeTable
需要一些苹果,以及一些将苹果映射到列的函数。列可以映射到苹果上的实际字段,也可以映射到动态计算的值(带有名称)。为了能够在单个Seq
中混合两个(实际字段和动态值),我定义了结构类型Col
。
要了解代码(如下)的行为,请尝试cols
makeTable
参数的以下变体:
// OK:
cols = Seq(_.isDone)
cols = Seq(job => dynCol1)
cols = Seq(job => dynCol1, job => dynCol2)
// ERROR: found: Seq[Job => Object], required: Seq[Job => Test.Col]
cols = Seq(_.isDone, job => dynCol1)
cols = Seq(_.isDone, job => dynCol2)
cols = Seq(_.isDone, job => dynCol1, job => dynCol2)
...所以只要_.isDone
(即映射到物理字段的列)与列的任何其他“味道”混合,就会发生错误( CASE 1 )。一个人表现得很好;其他类型的色谱柱在单独或混合时也表现良好( CASE 2 )。
直观的解决方法:将cols
标记为Seq[Job => Col]
始终修复错误。
反直觉的解决方法:明确标记Seq
中Col
的函数返回值的任何,或任何<函数为Job => Col
,解决了这个问题。
代码:
import net.liftweb.record.{ Record, MetaRecord }
import net.liftweb.record.field.IntField
import scala.language.reflectiveCalls
class Job extends Record[Job] {
def meta = Job
object isDone extends IntField(this)
}
object Job extends Job with MetaRecord[Job]
object Test extends App {
type Col = { def name: String; def get: Any }
def makeTable[T](xs: Seq[T])(cols: Seq[T => Col]) = {
assert(xs.size >= 1)
val rows = xs map { x => cols { map { _(x).get } }
val header = cols map { _(xs.head).name }
(header +: rows)
}
val dynCol1 = new { def name = "dyncol1"; def get = "dyn1" }
val dynCol2 = new { def name = "dyncol2"; def get = "dyn2" }
val jobs = Seq(Job.createRecord, Job.createRecord)
makeTable(jobs)(Seq(
_.isDone,
job => dynCol1,
job => dynCol2
))
}
P.S。我没有添加电梯或电梯记录标签,因为我认为这与Lift无关,而且只是一个由特定电梯情况触发的Scala问题。如果我错了,请随意纠正我。