匹配/案例的路径依赖类型

时间:2016-01-20 18:43:14

标签: scala path-dependent-type

sealed trait Desc {
  type T
}

trait Dataset[A] {
  def toDS[A] = new Dataset[A] {}
}
trait DataFrame {}


sealed trait DFDesc extends Desc {
  type T = Dummy
}

sealed trait DSDesc[A] extends Desc {
  type T = A
}

trait JobConstruction {
  def apply(desc: Desc): Job[desc.T]
}

sealed trait Job[DescType] {
  def description: Desc { type T = DescType }
}

abstract class DSJob[V] extends Job[V] {
  def result(con: JobConstruction): Dataset[V]
}

abstract class DFJob extends Job[Dummy] {
  def result(con: JobConstruction): DataFrame
}

trait Dummy {}

case class SampleDFDesc() extends DFDesc
case class SampleDFJob(description: SampleDFDesc) extends DFJob {
  override def result(con: JobConstruction) = new DataFrame {}
}
case class SampleDSDesc() extends DSDesc[Int]
case class SampleDSJob(description: SampleDSDesc) extends DSJob[Int] {
  override def result(con: JobConstruction) = new Dataset[Int] {}
}

object Main {
  val sampleConst = new JobConstruction {
    override def apply(desc: Desc): Job[desc.T] = desc match {
      case desc2: SampleDFDesc => SampleDFJob(desc2)
      case desc2: SampleDSDesc => SampleDSJob(desc2)
    }
  }
}

无法使用

进行编译
/tmp/sample.scala:73: error: type mismatch;
found   : this.SampleDFJob
required: this.Job[desc.T]
      case desc2: SampleDFDesc => SampleDFJob(desc2)
                                            ^
/tmp/sample.scala:74: error: type mismatch;
found   : this.SampleDSJob
required: this.Job[desc.T]
      case desc2: SampleDSDesc => SampleDSJob(desc2)

编辑:

我想让它以某种方式起作用:

case class SampleDepDesc(df: SampleDFDesc) extends DSDesc[Int]
case class SampleDepJob(description: SampleDepDesc) extends DSJob[Int] {
  override def result(con: JobConstruction): Dataset[Int] = con(description.df).result(con).toDS[Int]
}

2 个答案:

答案 0 :(得分:2)

问题分析

如果你这样编写sampleConst,错误就会以更有趣的方式形成:

object Main {
  val sampleConst = new JobConstruction {
    override def apply(desc: Desc): Job[desc.T] = {
      val result = desc match {
        case desc2: SampleDFDesc => SampleDFJob(desc2)
        case desc2: SampleDSDesc => SampleDSJob(desc2)
      }
      result
    }
}

错误消息变为:

type mismatch;
found   : Product with Serializable with main.Job[_ >: main.Dummy with Int]{def description: Product with Serializable with main.Desc{type T >: main.Dummy with Int}}
required: main.Job[desc.T]
Note: Any >: desc.T (and Product with Serializable with main.Job[_ >: main.Dummy with Int]{def description: Product with Serializable with main.Desc{type T >: main.Dummy with Int}} <: main.Job[_ >: main.Dummy with Int]), but trait Job is invariant in type DescType. You may wish to define DescType as -DescType instead. (SLS 4.5)

此消息难以阅读。原因似乎是逆向问题,如第四行所述,但让我们先尝试使这个错误信息可读。

这条消息如此长的原因是Scala正在做很多体操,以便理解所有类型的铸件和遗产。我们将(暂时)扁平化类型层次结构,以便在所有这些中看得更清楚。

此处,中间类SampleDFJobSampleDSJobSampleDFDescSampleDSDesc已被删除:

sealed trait Desc {
  type T
}

sealed trait DFDesc extends Desc {
  type T = Dummy
}

sealed trait DSDesc[A] extends Desc {
  type T = A
}

trait JobConstruction {
  def apply(desc: Desc): Job[desc.T]
}

sealed trait Job[DescType] {
  def description: Desc { type T = DescType }
}

class DSJob[V] extends Job[V]

class DFJob extends Job[Dummy]

trait Dummy {}

object Main {
  val sampleConst = new JobConstruction {
    override def apply(desc: Desc): Job[desc.T] = {
      val result = desc match {
        case desc2: DFDesc => new DFJob
        case desc2: DSDesc[Int] => new DSJob[Int]
      }
      result
    }
  }
}

现在出现错误信息:

type mismatch;
found   : main.Job[_1] where type _1 >: main.Dummy with Int
required: main.Job[desc.T]

问题似乎是Scala无法将main.Job[_ >: main.Dummy with Int]投射到desc.T

注意:为什么这种奇怪的类型?那么,result的泛型类型会有所不同,具体取决于模式匹配的情况(在第一种情况下,我们有Dummy,在第二种情况下,我们有一个Int )。由于Scala是静态类型的(至少在编译期间),它将尝试组成一个返回类型,它是所有可能类型的“公分母”(或者更确切地说是父类型)。它找到的最好的东西是_ >: main.Dummy with Int,它是“在模式匹配中找到的任何类型的父类型的任何类型”(main.DummyInt)。

为什么它不起作用

我认为此类型无法强制转换为desc.T的原因是Scala无法在编译时确认返回的类型始终为相同(自{{1 }}是不变的} DescType。实际上,Job[desc.T]来自desc.TSampleDFDesc.T,而返回类型将为SampleDSDesc.T,并且没有任何内容可以保证这两种类型相同(如果DescType }而不是SampleDSJob?)

解决方案

我认为不可能完全按照您尝试的方式进行编码,但您可以尝试... 避开问题:

如果您确定每个案例的返回类型始终与DSJob[String]的类型相同,那么您可以使用desc.T指定显式广播,如下所示:

asInstanceOf

当然,这不是类型安全的。

或者,如果您可以设法编写object Main { val sampleConst = new JobConstruction { override def apply(desc: Desc): Job[desc.T] = (desc match { case desc2: SampleDFDesc => SampleDFJob(desc2) case desc2: SampleDSDesc => SampleDSJob(desc2) }).asInstanceOf[Job[desc.T]] } } 类以便Job可以是逆变(DescType),则可以编写-DescType方法以获得以下内容签名代替:

Job.apply

答案 1 :(得分:0)

这不是一个真正的解决方案,但是您可以使用类型参数替换内部特征类型,此代码编译:

sealed trait Desc[T]

trait Dataset[A]
trait DataFrame

sealed trait DFDesc extends Desc[Dummy]

sealed trait DSDesc[A] extends Desc[A]

trait JobConstruction {
  def apply[A](desc: Desc[A]): Job[A]
}

sealed trait Job[A] {
  def description: Desc[A]
}

abstract class DSJob[V] extends Job[V] {
  def result: Dataset[V]
}

abstract class DFJob extends Job[Dummy] {
  def result: DataFrame
}

trait Dummy

case class SampleDFDesc() extends DFDesc
case class SampleDFJob(description: SampleDFDesc) extends DFJob {
  def result = new DataFrame {}
}
case class SampleDSDesc() extends DSDesc[Int]
case class SampleDSJob(description: SampleDSDesc) extends DSJob[Int] {
  def result = new Dataset[Int] {}
}

val sampleConst = new JobConstruction {
  override def apply[A](desc: Desc[A]): Job[A] = desc match {
    case desc2: SampleDFDesc => SampleDFJob(desc2)
    case desc2: SampleDSDesc => SampleDSJob(desc2)
  }
}

至于如何使路径依赖类型起作用,我很好奇。