具有上限类型的案例类

时间:2009-12-27 01:55:14

标签: scala types

我遇到了一个使用case类和带有上限类型的参数化类型的问题。 Scala编译器告诉我它找到了类型DefaultEdge但需要Edge[Type]。我尝试使用像case DefaultEdge[Type]之类的东西,但是我遇到了语法错误。

这是我的设置。我有几个对应不同类型的边缘案例类。这些类包含参数化类型V.

object EdgeKind extends Enumeration {
  type EdgeKind = Value
  val Default, Jump, True, False, DefaultCase, Case, Throw, Return = Value
}

sealed abstract class Edge[V <: VertexLike](val startVertex: V, val endVertex: V, val kind: EdgeKind.EdgeKind)

case class DefaultEdge[V <: VertexLike](override val startVertex: V, override val endVertex: V)
  extends Edge[V](startVertex, endVertex, EdgeKind.Default)
case class JumpEdge[V <: VertexLike](//...

然后我有一个名为GraphLike的特征,它定义了几种方法。唯一有趣的部分应该是这个:

trait GraphLike[V <: VertexLike] {
  protected type E <: Edge[V]
}

中间的另一个特征是实现GraphLike特征的一些方法,称为GraphLikeWithAdjacencyMatrix。当我把所有东西连在一起时,我有以下课程:

class CFG extends GraphLikeWithAdjacencyMatrix[BasicBlockVertex] {
  def dotExport = {
    def vertexToString(vertex: BasicBlockVertex) = ""
    def edgeToString(edge: E) = edge match {//also tried Edge[BasicBlockVertex] here
      case DefaultEdge => error("CFG may not contain default edges.")
      case JumpEdge => "jump"
      case TrueEdge => "true"
      case FalseEdge => "false"
      case DefaultCaseEdge => "default"
      case CaseEdge => "case"
      case ThrowEdge => "throw"
      case ReturnEdge => "return"
    }
    new DOTExport(this, vertexToString, edgeToString)
  }
}

这是我遇到问题的地方。我被告知Edge [BasicBlockVertex]是预期的,我只提供一个DefaultEdge。 DOTExport中的定义是class DOTExport[V <: VertexLike](val graph: GraphLike[V], val vertexToString: V => String, val edgeToString: Edge[V] => String)

所以现在我的问题是,我怎么还能为边类型使用case类并使编译器满意?我一定是个愚蠢的错误。

顺便说一句,匹配代码工作一次我说DefaultEdge(x,y)而不是DefaultCase等。然而DOTExport的实例化失败,因为Edge [?]是必需的并且我通过了CFG.E

谢谢!

编辑:事实上,GraphLike中的E = Edge[V]与使用DefaultEdge(_, _)的组合有效。不幸的是,这只是尝试和错误的结果。我真的很想知道为什么它现在有效。

错误消息:

(fragment of test.scala):25: error:
type mismatch;  found   :
(Graph.this.E) => java.lang.String 
required: (this.Edge[?]) => String
    new DOTExport(this, (vertex: V) => vertex.toString, edgeToString)

以下是完整的可编译代码,说明了我的问题。同样,我的问题是第14行,因为当您使用type E <: Edge[V]替换type E = Edge[V]并且我不知道原因时,一切正常。

object EdgeKind {
  val Default = 0
  val Jump = 1
}

abstract class Edge[V <: VertexLike](val startVertex: V, val endVertex: V, val kind: Int)

case class DefaultEdge[V <: VertexLike](override val startVertex: V, override val endVertex: V) extends Edge[V](startVertex, endVertex, EdgeKind.Default)
case class JumpEdge[V <: VertexLike](override val startVertex: V, override val endVertex: V) extends Edge[V](startVertex, endVertex, EdgeKind.Jump)

trait VertexLike

trait GraphLike[V <: VertexLike] {
  protected type E <: Edge[V] // Everything works when E = Edge[V]
}

class DOTExport[V <: VertexLike](val graph: GraphLike[V], val vertexToString: V => String, val edgeToString: Edge[V] => String)

class Graph[V <: VertexLike] extends GraphLike[V] {
  def dotExport = {
    def edgeToString(edge: E) = edge match {
      case DefaultEdge(_, _) => ""
      case JumpEdge(_, _) => "jump"
    }
    new DOTExport(this, (vertex: V) => vertex.toString, edgeToString)
  }
}

2 个答案:

答案 0 :(得分:3)

缺少太多能够真正帮助的人。您必须提供确切的错误消息,而不是解释它们。

无论如何,case DefaultEdge表示传递的对象与对象DefaultEdge之间的比较。后者是类DefaultEdge的对象伴侣,通过使用case class语句自动创建。此类伴随对象不属于属于它们所属的类。他们是单身人士,这意味着他们自己的班级本身就是独一无二的,否则,只是继承AnyRef

因此,换句话说,DefaultEdge不是Edge,这就是您收到错误的原因。至于使用DefaultEdge(_, _)时出现的错误,你省略了太多细节。但是......你确定你这样编写代码吗?我希望以下内容:

new DOTExport(this, vertexToString _, edgeToString _)

修改

好的,第二条错误信息现在已经清楚了。 E的原始声明是Edge子类,但是DOTExport期待一个带Edge的函数并转换它进入String。要了解此处的问题,请注意以下定义也适用:

protected type E >: Edge[V]

让我们说,为了说明问题,您有两个Edge的子类:IntEdgeStringEdge。第一个字段为number字段,第二个字段为name字段。因此,我们可以编写以下函数:

def intEdgeToString(ie: IntEdge) = ie.number.toString
def stringEdgeToString(se: StringEdge) = se.name

现在,让我们创建一个var并存储其中一个:

var eTS: E => String = intEdgeToString _

由于EEdge的任何子类,因此这是可以接受的。因此,我们为其创建DOTExport传递eTS。接下来,我们将DOTExport提供给IntEdge,而不是StringEdge。由于后者没有number字段,尝试运行它会在运行时导致异常,这会破坏静态类型的整个目的。

这是为了防止Scala不接受您的原始定义的这类问题。

答案 1 :(得分:2)

首先,Daniel非常正确,拥有更准确的信息会有很大帮助。但是,看起来你只需要同时做两件事:

def edgeToString(edge: Edge[BasicBlockVertex]) = edge match {
  case DefaultEdge(_,_) => error("CFG may not contain default edges.")
  case JumpEdge(_,_) => "jump"
  case TrueEdge(_,_) => "true"
  ...

因为1)模式DefaultEdge匹配错误的内容2)edgeToString(edge: E) = ...表示edgeToString在用作函数值而不是CFG.E => String时具有类型Edge[V] => String所以不能传递给new DOTExport