2012-02-08 23 views
8

Estoy tratando de entender los auto tipos abstractos y explícitos en scala. Vamos a considerar este ejemplo: Quiero crear una base para el árbol extensible tan simple como esto:auto tipo Scala y this.type in collections issue

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

Sin embargo, quiero ser capaz de extender los nodos del árbol con algunos métodos y utilizar estos métodos como: tree.children foreach { _.newMethod() }

Para ello he intentado:

A. this.type fall

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

Trabajando una variante re bastante torpe

B. Resumen tipos fall

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

no funciona en absoluto debido a camino de coincidencia de tipos específicos que he entendido.

params C. Tipo (genéricos): OK

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

    def children: Iterable[Node] 

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

funciona bien, pero no tan bueno para mantener en las clases derivadas.

¿Alguna idea de cómo hacer que las dos primeras variantes funcionen sin toneladas de código?

Además, con this.type me he encontrado con problemas con la implementación.

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

¡Gracias!

+3

Ah sí. El problema "Scala no tiene MyType" otra vez. –

+0

como puedes ver, eché un vistazo a esto en SO, y probé las variantes propuestas. funciona bien para construcciones bastante simples (como 'c.incr(). decr()' ejemplo en el documento de Martin), pero con colecciones no lo hace. – tuxSlayer

+1

sí. entendió por qué después de leer su discusión aquí http://www.scala-lang.org/node/6649, gracias – tuxSlayer

Respuesta

1

Al final me he decidido, con lo que se propone en esta discusión 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 }) 
    } 
} 

es decir, variante (C) pero con auto tipo explícito. Esto le da la oportunidad de usar this en otros métodos (por ejemplo, método find(path: String): Option[Node]).

5

sin comprender realmente lo que el problema es que tienes con (C) podría intentar una variante de (B):

trait Tree { 
    type Node <: Tree 

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

que evita el problema tipo específico ruta. Por cierto que realmente debería echar un vistazo a http://www.assembla.com/spaces/scala-graph/wiki

+1

¡Oh, gracias!, olvidé esta notación de selector de tipo. Como para (C) - Agregaré estos tipos de argumentos a todas las clases secundarias. Esto no es muy conveniente. – tuxSlayer