2012-02-08 10 views
8

Próbuję owijać głowę wokół abstrakcyjnych i jawnych typów siebie w scala. Rozważmy następujący przykład: Chcę stworzyć bazę dla rozszerzalny drzewa tak proste, jak to:Scala self type i this.type w kolekcjach wydanie

trait Tree { 
    def children: Iterable[Tree] 
    def descendants: Iterable[Tree] = { val dv = children.view; dv ++ (dv.flatMap { _.children }) } 
} 

Jednakże chcę, aby móc rozszerzyć węzły drzewa z niektórych metod i korzystać z tych metod, takich jak: tree.children foreach { _.newMethod() }

Do tego próbowałem:

A. this.type: FAIL

trait Tree { 
    def children: Iterable[this.type] 
    def descendants: Iterable[this.type] = { 
     val dv = children.view 
     // FAIL: type mismatch; found : scala.collection.IterableView[com.abovobo.data.Tree,Iterable[_]] required: Iterable[Tree.this.type] 
     // dv ++ (dv.flatMap { _.children }) 
     // OK: 
     dv.++[this.type, Iterable[this.type]](dv.flatMap[this.type, Iterable[this.type]]{ _.children }) 
    } 
} 

Praca wariant A jestem całkiem niezgrabny.

B. abstrakcyjne typy: FAIL

trait Tree { 
    type Node <: Tree 

    def children: Iterable[Node] 
    def descendants: Iterable[Node] = { 
     val dv = children.view 
     // FAIL: type mismatch; found : scala.collection.IterableView[com.abovobo.data.Tree#Node,Iterable[_]] required: Iterable[Tree.this.Node] 
     dv ++ (dv.flatMap { _.children }) 
    } 
} 

nie działa w ogóle z powodu niedopasowania ścieżki konkretnego typu jak zrozumiałem.

C. Rodzaj params (rodzajów): OK

trait Tree[+Node <: Tree[Node]] { 

    def children: Iterable[Node] 

    def descendants: Iterable[Node] = { 
     val dv = children.view 
     dv ++ (dv.flatMap { _.children }) 
    } 
} 

działa OK, ale nie tak dobre, aby utrzymać w klasach pochodnych.

Jakieś pomysły, jak zrobić pierwsze dwa warianty pracy bez mnóstwa kodu?

Również z tym typem mam problemy z implementacją.

trait BiDTree extends Tree { 
    def parent: Option[this.type] 
} 

// how to accept this param? Option[TreeImpl] doesn't work. 
class TreeImpl(val parent: Option[???]) extends BiDTree { 
    // ... 
} 

Dzięki!

+3

Ah tak. Problem "Scala nie ma MyType". –

+0

jak widać, rzuciłem okiem na to w SO i wypróbowałem proponowane warianty. działa dobrze dla całkiem prostych konstrukcji (jak np. 'c.incr(). decr()' w artykule Martina), ale z kolekcjami tak nie jest. – tuxSlayer

+1

tak. zrozumiałeś dlaczego po przeczytaniu twojej dyskusji http://www.scala-lang.org/node/6649, dziękuje – tuxSlayer

Odpowiedz

1

Pod koniec mam rozliczone z tym, co zostało zaproponowane w tej dyskusji http://www.scala-lang.org/node/6649:

trait Tree[+Node <: Tree[Node]] { 
    this: Node => 

    def children: Iterable[Node] 

    def descendants: Iterable[Node] = { 
     val dv = children.view 
     dv ++ (dv.flatMap { _.children }) 
    } 
} 

Tj wariant (C), ale z wyraźnym rodzajem siebie. Daje to szansę użycia this w innych metodach (powiedzmy, metoda find(path: String): Option[Node]).

5

nie rozumiejąc czym jest problem masz z (C) można spróbować wariantu (B):

trait Tree { 
    type Node <: Tree 

    def children: Iterable[Tree#Node] 
    def descendants: Iterable[Tree#Node] = { 
     val dv = children.view 
     dv ++ (dv.flatMap { _.children }) 
    } 
} 

omijająca konkretnego problemu ścieżka typu. Nawiasem mówiąc naprawdę powinieneś przyjrzeć się http://www.assembla.com/spaces/scala-graph/wiki

+1

Oh, dzięki, spóźniłeś się z notacją tego typu. Co do (C) - Dodam argumenty tego typu do wszystkich klas potomnych. To nie jest bardzo wygodne. – tuxSlayer