2010-01-14 21 views
9

Próbuję użyć odpowiedzi a preceding question, aby zaimplementować małą bibliotekę wykresów. Chodzi o to, aby rozważyć wykresy jako kolektory, w których wierzchołki zawijają elementy kolekcji.Parametry typu mieszania i typy abstrakcyjne w scala

Chciałbym użyć typów abstrakcyjnych do reprezentowania typów Vertex i Edge (ze względu na bezpieczeństwo typów) i chcę użyć parametrów typu do przedstawienia typu elementów kolekcji (ponieważ chcę je łatwo zdefiniować podczas tworzenia instancji).

Jednak próbując najbardziej podstawowy przykład, o którym mogę myśleć, utknąłem z błędami kompilacji. Oto przykład:

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 
    } 

} 

A oto podstawowe implementacje:

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 + ")" 
    } 

} 

Kiedy próbuje skompilować, otrzymuję:

/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 

mam absolutnie żadnego pojęcia o znaczeniu te błędy ... Jeśli jednak specjalizuję się w typie T w implementacji (class SimpleGraphKind extends GraphKind[Int], otrzymuję tylko pierwszy błąd:

Masz jakieś pomysły?

+0

Czy możesz wyjaśnić, dlaczego chcesz, aby wykres był zbiorem? –

+0

Jedną z aplikacji tej biblioteki byłoby zaimplementowanie rodzaju automatów komórkowych na wykresie (drugim jest złożone badanie sieci). Wtedy może być miło uzyskać dostęp bezpośrednio do obiektów Cell zamkniętych w wierzchołkach ... Ale jeśli masz pomysł na rozwiązanie bez wykresu jako funkcji kolekcji, również mnie to interesuje. – paradigmatic

+0

Nadal nie jestem pewien, czy widzę połączenie. W jaki sposób odróżnisz swoją bibliotekę graficzną od, powiedzmy, JGraphT? Czy jest to funkcjonalne podejście do wykresów? –

Odpowiedz

11

Kompilacja ta z -explaintypes plonów:

<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 

Teraz miałem napisać Nie rozumiem jak T <: T może być fałszywy - to prawie jak T zdefiniowano dwa razy, co oczywiście stanowi cały problem. Tutaj:

abstract class GraphKind[T] { 

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

    def newGraph(): G 

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

Ok, klasa GraphKind jest parametryzowane z T i wpisz G musi być Graph[T]. Teraz sparametryzowana jest również klasa Graph, a jej parametr jest również nazywany T. Aby zapobiec dezorientacji, przepiszmy:

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] 
    } 

Zauważ, że to jest DOKŁADNIE równe temu, co napisałeś. Używam tylko innej nazwy parametru typu, aby nie mylić z T parametryzowaniem GraphKind.

Tak, tu jest logika:

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

co oznacza, że ​​

Graph[T2] <: Graph[T] 

A ponieważ Graph rozciąga Collection:

Collection[T2] <: Collection[T] 

Ale nie ma gwarancji, że jest to prawdziwe.Nie rozumiem, dlaczego problem nie pojawia się, gdy dziedziczenie nie jest obecne. Fix:

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 + ")" 
    } 
} 

Od Vertex i Graph zostanie przywiązany do jednej instancji GraphKind, następnie T zostanie ustalona na cokolwiek to było określone dla tej instancji. Na przykład:

scala> new SimpleGraphKind[Int] 
res0: SimpleGraphKind[Int] = [email protected] 

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") 
       ^
+0

Dziękuję bardzo. Muszę wyznać, że jestem zawstydzony, ponieważ popełniłem podobny błąd, gdy uczyłem się generyków w java 5 lat temu ... – paradigmatic

+1

Nie ma potrzeby wstydź się, ja też to robię, a potem nienawidzę siebie za to, że to robię Nie chodzi o to, że nie wiem * jak to działa, po prostu naturalnie jest pisać 'D [T] rozszerza C [T]'. –

+0

Z drugiej strony, '-explaintypes' jest twoim przyjacielem, trochę czasu na przyzwyczajenie się do niego, zwłaszcza, że ​​typy się zmieniają, gdy przeskakują z wariantu co-variantowego do pozycji przeciwnej, jednak nie ma to jak dla złożonych błędów typu. –

1

Wygląda na to, że wierzchołki należące do określonego wykresu i tylko ten wykres mogą być najlepiej reprezentowane w systemie typów z zagnieżdżoną cechą wierzchołków na wykresie. Czy to, co próbujesz osiągnąć, spotkało się z następującą strukturą?

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] 
}