2012-02-08 15 views
8

Sto cercando di avvolgere la mia mente attorno a tipi di sé astratti ed espliciti in scala. Consente di considerare questo esempio: Voglio creare una base per l'albero estensibile semplice come questo:Tipo Scala di tipo e tipo this.type in collezioni

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

Tuttavia, voglio essere in grado di estendere nodi dell'albero con alcuni metodi e utilizzare questi metodi come: tree.children foreach { _.newMethod() }

Per questo ho provato:

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

Lavorare variante a piuttosto maldestro.

B. I tipi astratti: 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 }) 
    } 
} 

non funziona affatto a causa di percorso specifico tipo non corrispondente come ho capito.

C. Tipo params (generici): ok

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

    def children: Iterable[Node] 

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

funziona bene, ma non così buono da mantenere nelle classi derivate.

Qualche idea su come far funzionare le prime due varianti senza un sacco di codice?

Inoltre, con questo tipo di problemi ho riscontrato problemi con l'implementazione.

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 { 
    // ... 
} 

Grazie!

+3

Ah sì. Il problema "Scala non ha MyType" di nuovo. –

+0

come puoi vedere Ho dato un'occhiata a questo in SO, e ho provato le varianti proposte. funziona bene per costrutti abbastanza semplici (come 'c.incr(). decr()' esempio nel documento di Martin), ma con le raccolte no. – tuxSlayer

+1

sì. capito il motivo per cui dopo aver letto la tua discussione qui http://www.scala-lang.org/node/6649, grazie – tuxSlayer

risposta

1

Alla fine ho risolto con ciò che è stato proposto in questa discussione 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 }) 
    } 
} 

Vale a dire variante (C) ma con tipo di auto esplicito. Questo dà la possibilità di usare this in altri metodi (per esempio, metodo find(path: String): Option[Node]).

5

senza realmente capire qual è il problema che hai con (C) si potrebbe provare una variante di (B):

trait Tree { 
    type Node <: Tree 

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

che evita il vostro percorso specifico problema tipo. Tra l'altro si dovrebbe davvero dare un'occhiata a http://www.assembla.com/spaces/scala-graph/wiki

+1

Oh, grazie, mi mancava questo tipo di notazione selettore. Per quanto riguarda (C) - Devo aggiungere questo tipo di argomenti a tutte le classi di bambini. Questo non è molto conveniente. – tuxSlayer