การผสมพารามิเตอร์ประเภทและประเภทนามธรรมในสกาลา

ฉันกำลังพยายามใช้คำตอบของคำถามก่อนหน้าเพื่อใช้กราฟขนาดเล็ก ห้องสมุด. แนวคิดก็คือการพิจารณากราฟเป็นคอลเลคชัน โดยที่จุดยอดจะล้อมองค์ประกอบคอลเลกชั่น

ฉันต้องการใช้ประเภทนามธรรมเพื่อแสดงประเภท Vertex และ Edge (เนื่องจากความปลอดภัยของประเภท) และฉันต้องการใช้พารามิเตอร์ประเภทเพื่อแสดงประเภทขององค์ประกอบการรวบรวม (เพราะฉันต้องการกำหนดพวกมันในการสร้างอินสแตนซ์อย่างง่ายดาย)

อย่างไรก็ตาม เมื่อลองตัวอย่างพื้นฐานที่สุดที่ฉันคิดได้ ฉันติดอยู่กับข้อผิดพลาดในการคอมไพล์ นี่คือตัวอย่าง:

package graph

abstract class GraphKind[T] {

  type V <: Vertex[T]
  type G <: Graph[T]

  def newGraph(): G

  abstract class Graph[T] extends Collection[T]{
    self: G =>
    def vertices(): List[V]
    def add(t: T): Unit
    def size(): Int
    def elements(): Iterator[T]
  }

  trait Vertex[T] {
    self: V =>
      def graph(): G
      def value(): T
  }

}

และนี่คือการใช้งานพื้นฐาน:

class SimpleGraphKind[T] extends GraphKind[T] {

  type G = GraphImpl[T]
  type V = VertexImpl[T]

  def newGraph() = new GraphImpl[T]

  class GraphImpl[T] extends Graph[T] {
    private var vertices_ = List[V]()
    def vertices = vertices_
    def add( t: T ) {  vertices_ ::= new VertexImpl[T](t,this) }
    def size() = vertices_.size
    def elements() = vertices.map( _.value ).elements
  }

  class VertexImpl[T](val value: T, val graph: GraphImpl[T]) extends Vertex[T] {
    override lazy val toString = "Vertex(" + value.toString + ")"
  }

}

เมื่อพยายามคอมไพล์ ฉันจะได้รับ:

/prg/ScalaGraph/study/Graph.scala:10: error: illegal inheritance;
 self-type GraphKind.this.G does not conform to Collection[T]'s selftype Collection[T]
  abstract class Graph[T] extends Collection[T]{
                              ^
/prg/ScalaGraph/study/Graph.scala:33: error: illegal inheritance;
 self-type SimpleGraphKind.this.GraphImpl[T] does not conform to   SimpleGraphKind.this.Graph[T]'s selftype SimpleGraphKind.this.G
  class GraphImpl[T] extends Graph[T] {
                         ^
/prg/ScalaGraph/study/Graph.scala:36: error: type mismatch;
 found   : SimpleGraphKind.this.VertexImpl[T]
 required: SimpleGraphKind.this.V
    def add( t: T ) {  vertices_ ::= new VertexImpl[T](t,this) }
                                 ^
/prg/ScalaGraph/study/Graph.scala:38: error: type mismatch;
 found   : Iterator[T(in class SimpleGraphKind)]
 required: Iterator[T(in class GraphImpl)]
    def elements() = vertices.map( _.value ).elements
                                         ^
/prg/ScalaGraph/study/Graph.scala:41: error: illegal inheritance;
 self-type SimpleGraphKind.this.VertexImpl[T] does not conform to   SimpleGraphKind.this.Vertex[T]'s selftype SimpleGraphKind.this.V
  class VertexImpl[T](val value: T, val graph: GraphImpl[T]) extends Vertex[T] {
                                                                 ^
5 errors found

ฉันไม่รู้ความหมายของข้อผิดพลาดเหล่านี้เลย... อย่างไรก็ตาม หากฉันเชี่ยวชาญประเภท T ในการใช้งาน (class SimpleGraphKind extends GraphKind[Int] ฉันจะพบเฉพาะข้อผิดพลาดแรกเท่านั้น

คุณมีความคิดบ้างไหม?


person paradigmatic    schedule 14.01.2010    source แหล่งที่มา
comment
คุณช่วยอธิบายได้ไหมว่าทำไมคุณถึงต้องการให้กราฟเป็นคอลเล็กชั่น?   -  person Randall Schulz    schedule 14.01.2010
comment
แอปพลิเคชันหนึ่งของไลบรารีนี้คือการใช้ออโตมาตะเซลลูลาร์ชนิดหนึ่งบนกราฟ (อีกอันคือการวิจัยเครือข่ายที่ซับซ้อน) การเข้าถึงวัตถุ Cell ที่อยู่ในจุดยอดโดยตรงอาจเป็นเรื่องดีได้... แต่ถ้าคุณมีแนวคิดในการแก้ปัญหาโดยไม่ต้องใช้กราฟเป็นคุณลักษณะการรวบรวม ฉันก็สนใจเช่นกัน   -  person paradigmatic    schedule 14.01.2010
comment
ฉันยังไม่แน่ใจว่าฉันเห็นการเชื่อมต่อหรือไม่ คุณเห็นว่าไลบรารีกราฟของคุณแตกต่างจาก JGraphT อย่างไร มันเป็นแนวทางการทำงานของกราฟหรือไม่?   -  person Randall Schulz    schedule 14.01.2010
comment
JGraphT (หรือ Jung) ไม่ได้ผูกกับประเภทจุดยอดและขอบ ดังนั้นคุณต้องพึ่งพาวัตถุกราฟเพื่อดำเนินการกับกราฟ ในทางตรงกันข้าม ฉันพบว่าการจัดการวัตถุขอบและจุดยอดโดยตรงเป็นธรรมชาติมากกว่า (เช่น ฉันชอบ vertex1.connectTo(vertex2) หรือ vertex1 -> vertex2 มากกว่า graph.connect( vertex1, vertex2) จุดยอดอาจมีตรรกะหรือเป็นเพียงพร็อกซีไปยังวัตถุกราฟ ขึ้นอยู่กับการใช้งาน   -  person paradigmatic    schedule 15.01.2010


คำตอบ (2)


รวบรวมสิ่งนี้ด้วย -explaintypes อัตราผลตอบแทน:

<console>:11: error: illegal inheritance;
 self-type GraphKind.this.G does not conform to Collection[T]'s selftype Collection[T]
         abstract class Graph[T] extends Collection[T]{
                                         ^
    GraphKind.this.G <: Collection[T]?
      Iterable[T] <: Iterable[T]?
        T <: T?
          T <: Nothing?
            <notype> <: Nothing?
            false
            Any <: Nothing?
              <notype> <: Nothing?
              false
            false
          false
          Any <: T?
            Any <: Nothing?
              <notype> <: Nothing?
              false
            false
          false
        false
      false
      GraphKind.this.Graph[T] <: Iterable[T]?
        Iterable[T] <: Iterable[T]?
          T <: T?
            T <: Nothing?
              <notype> <: Nothing?
              false
              Any <: Nothing?
                <notype> <: Nothing?
                false
              false
            false
            Any <: T?
              Any <: Nothing?
                <notype> <: Nothing?
                false
              false
            false
          false
        false
      false
    false

ตอนนี้ ฉันกำลังจะเขียนว่า ฉันไม่เข้าใจว่า T <: T อาจเป็นเท็จได้อย่างไร เกือบจะเหมือนกับว่า T ถูกกำหนดไว้สองครั้ง ซึ่งแน่นอนว่าเป็นปัญหาทั้งหมด ที่นี่:

abstract class GraphKind[T] { 

  type V <: Vertex[T] 
  type G <: Graph[T] 

  def newGraph(): G 

  abstract class Graph[T] extends Collection[T]{ 

ตกลง คลาส GraphKind ได้รับการกำหนดพารามิเตอร์ด้วย T และประเภท G ต้องเป็น Graph[T] ตอนนี้ คลาส Graph ก็มีการกำหนดพารามิเตอร์เช่นกัน และพารามิเตอร์ของคลาสนั้นก็เรียกว่า T เพื่อป้องกันความสับสน ให้เขียนใหม่:

  abstract class Graph[T2] extends Collection[T2]{
    self: G =>
    def vertices(): List[V]
    def add(t: T2): Unit
    def size(): Int
    def elements(): Iterator[T2]
  }

โปรดทราบว่านี่เท่ากับสิ่งที่คุณเขียนทุกประการ ฉันแค่ใช้ชื่ออื่นสำหรับพารามิเตอร์ type เพื่อไม่ให้สับสนกับ T ที่กำลังกำหนดพารามิเตอร์ GraphKind

นี่คือตรรกะ:

G <: Graph[T]
Graph[T2] <: Collection[T2]
Graph[T2] <: G  // self type

ซึ่งหมายความว่า

Graph[T2] <: Graph[T]

และเนื่องจาก Graph ขยาย Collection:

Collection[T2] <: Collection[T]

แต่ไม่มีการรับประกันว่าสิ่งนี้จะเป็นความจริง ฉันไม่เข้าใจว่าเหตุใดปัญหาจึงไม่ปรากฏขึ้นเมื่อไม่มีมรดก แก้ไข:

abstract class GraphKind[T] {

  type V <: Vertex
  type G <: Graph

  def newGraph(): G

  abstract class Graph extends Collection[T]{
    self: G =>
    def vertices(): List[V]
    def add(t: T): Unit
    def size(): Int
    def elements(): Iterator[T]
  }

  trait Vertex {
    self: V =>
      def graph(): G
      def value(): T
  }

}

class SimpleGraphKind[T] extends GraphKind[T] {

  type G = GraphImpl
  type V = VertexImpl

  def newGraph() = new GraphImpl

  class GraphImpl extends Graph {
    private var vertices_ = List[V]()
    def vertices = vertices_
    def add( t: T ) {  vertices_ ::= new VertexImpl(t,this) }
    override def size() = vertices_.size
    override def elements() = vertices.map( _.value ).elements
  }

  class VertexImpl(val value: T, val graph: GraphImpl) extends Vertex {
    override lazy val toString = "Vertex(" + value.toString + ")"
  }
}

เนื่องจาก Vertex และ Graph จะเชื่อมโยงกับหนึ่งอินสแตนซ์ของ GraphKind ดังนั้น T จะถูกคงที่กับอะไรก็ตามที่ถูกกำหนดไว้สำหรับอินสแตนซ์นั้น ตัวอย่างเช่น:

scala> new SimpleGraphKind[Int]
res0: SimpleGraphKind[Int] = SimpleGraphKind@1dd0fe7

scala> new res0.GraphImpl
res1: res0.GraphImpl = line10()

scala> res1.add(10)

scala> res1.add("abc")
<console>:9: error: type mismatch;
 found   : java.lang.String("abc")
 required: Int
       res1.add("abc")
                ^
person Daniel C. Sobral    schedule 14.01.2010
comment
ขอบคุณมาก. ฉันต้องสารภาพว่าฉันรู้สึกละอายใจเพราะฉันทำผิดพลาดเหมือนกันเมื่อเรียนยาสามัญใน java เมื่อ 5 ปีที่แล้ว... - person paradigmatic; 15.01.2010
comment
ไม่จำเป็นต้องละอายใจ ฉันก็ทำเหมือนกัน แล้วก็เกลียดตัวเองที่ทำแบบนั้น ไม่ใช่ว่าฉัน รู้ มันทำงานอย่างไร มันเป็นเพียงการเขียน D[T] extends C[T] เป็นเรื่องธรรมดา - person Daniel C. Sobral; 15.01.2010
comment
ในทางกลับกัน -explaintypes คือเพื่อนของคุณ ต้องใช้เวลาสักพักเพื่อทำความคุ้นเคย โดยเฉพาะอย่างยิ่งเมื่อประเภทต่างๆ เปลี่ยนไปเมื่อข้ามจากตัวแปรร่วมไปยังตำแหน่งที่ตรงกันข้าม อย่างไรก็ตาม ไม่มีอะไรที่เหมือนกับข้อผิดพลาดประเภทที่ซับซ้อน - person Daniel C. Sobral; 17.01.2010

ดูเหมือนว่า Vertex จะเป็นของกราฟใดกราฟหนึ่งและมีเพียงกราฟนั้นเท่านั้นที่สามารถแสดงได้ดีที่สุดในระบบประเภทที่มีลักษณะ Vertex ที่ซ้อนกันในกราฟ สิ่งที่คุณพยายามทำให้สำเร็จเป็นไปตามโครงสร้างต่อไปนี้หรือไม่

abstract class Graph[T] extends Collection[T] {
  thisGraph: Graph[T] =>

  def newGraph: Graph[T] 
  def vertices: List[Vertex[T]]
  def add(t: T): Unit
  def size: Int
  def elements: Iterator[T]

  trait Vertex[T] {
      def graph = thisGraph
      def value: T
  }
}

class SimpleGraph[T] extends Graph[T] {
  private[this] var verts = List[Vertex[T]]()

  def newGraph = new SimpleGraph[T]
  def vertices = verts
  def add(t: T) { verts ::= SimpleVertex(t) }
  override def size = verts.size
  override def elements = verts.map(_.value).elements
  def iterator = elements // the "new" elements in 2.8

  case class SimpleVertex[T](value: T) extends Vertex[T]
}
person Mitch Blevins    schedule 14.01.2010