为什么Scala类型推断在一种情况下失败而在另一种情况下失败?

时间:2014-02-06 00:02:13

标签: scala type-inference

后台:我正在使用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]始终修复错误。

反直觉的解决方法:明确标记SeqCol的函数返回值的任何,或任何<函数为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问题。如果我错了,请随意纠正我。

0 个答案:

没有答案