2011-04-12 11 views
5

Deje que haya clases Fruit, Orange y Apple.Pregunta acerca de las clases de tipo en Scala

abstract class Fruit 
class Orange extends Fruit 
class Apple extends Fruit

Ahora quiero agregar funcionalidad a write ambos tipos Orange y Apple. Utilizando el tipo de patrón de clase que pueda hacer lo siguiente:

trait Writer[T] {def write(t:T)} 

implicit object AppleWriter extends Writer[Apple] { 
    def write(a:Apple) {println("I am an apple!")} 
} 

implicit object OrangeWriter extends Writer[Orange] { 
    def write(o:Orange) {println("I am an orange!")} 
} 

def write[T](t:T)(implicit w:Writer[T]){w.write(t)}

Así que para, tan bueno, pero lo que si quiero definir writeFruits?

def writeFruits(fruits:List[Fruit]) {for (fruit <- fruits) write(fruit)}

me gustaría writeFruits a llamar a cualquiera write[Apple] o write[Orange] para cada fruit. Veo que no funciona (y sé por qué) pero quizás Puedo implementar el writeFruits de todos modos.

¿Puedo implementar writeFruits de alguna manera?

Respuesta

4

Debe seleccionar solo aquellos Fruit para los cuales existe Writer. Desafortunadamente, una vez que has lanzado al Fruit has perdido la capacidad de descubrir automáticamente cuál es cuál. Si tiene que configurar el problema de esta manera - en lugar de ensamblar una lista de frutas se puede escribir o algo por el estilo - a continuación, una opción razonable es la de dividir los tipos de nuevo con una FruitWriter:

def writeOne[T](t:T)(implicit w:Writer[T]){w.write(t)} // New name to avoid shadowing 

implicit object FruitWriter extends Writer[Fruit] { 
    def write(f: Fruit) { f match { 
    case o: Orange => writeOne(o) 
    case a: Apple => writeOne(a) 
    }} 
} 

scala> val fruits = List(new Apple, new Orange) 
fruits: List[Fruit] = List([email protected], [email protected]) 

scala> for (fruit <- fruits) writeOne(fruit) 
I am an apple! 
I am an orange! 
6

En el caso de covariantes/tipos contravariant, casi se necesita definir su tipo de clase en la "base" escriba aquí:

implicit object FruitWriter extends Writer[Fruit] { 
    def write(a : Fruit) = a match { 
    case _ : Apple => println("I am an apple!") 
    case _ : Orange => println("I am an orange") 
    } 
} 

También puede trabajar en la definición de la clase de tipo con una varianza de manera que escritor [Fruta] podría usarse cuando necesites un escritor [Apple]. Es desafortunado, pero si quieres usar el polimorfismo OO, debes codificarlo en los aspectos funcionales.

* fuerte de texto * Otra opción es utilizar un nodo Hlist para escribir-frutas y hacer todo el tipo de recursión mismo ...

Suponiendo:

trait HList 
object HNil extends HList 
case class ::[A, Rest <: HList](head : A, tail : Rest) 

Entonces podemos hacer algo divertirse como:

implicit def nilWriter = new Writer[HNil] = { def write(o : HNil) =() } 
implicit def hlistWriter[A, Rest](implicit aw : Writer[A], rw : Writer[Rest]) = 
    new Writer[A :: Rest] { 
    def write(o : (A :: Rest)) = { 
    aw.write(o.head) 
    rw.write(o.tail) 
    } 
} 

EMPRESA

write(new Orange :: new Apple :: HNil) 

Nota: No he probado este código, pero el concepto de tipos de expansión recursiva es el sonido. Realmente no estoy recomendando este enfoque.

2

O tal caso-clases son para ti?

abstract class Fruit {} 
case object Orange extends Fruit 
case object Apple extends Fruit 

trait Writer[T] {def write (t:T)} 

implicit object FruitWriter extends Writer [Fruit] { 
    def write (fruit: Fruit) = fruit match { 
    case Apple => println ("I am an apple!") 
    case Orange => println ("I am an orange!") 
    } 
} 

def writeFruits (fruits: List[Fruit]) { 
    for (fruit <- fruits) write(fruit) 
} 

val fl = List (Orange, Apple, Apple, Orange, Apple)  

writeFruits (fl)          
I am an orange! 
I am an apple! 
I am an apple! 
I am an orange! 
I am an apple! 
1

Esto no es exactamente lo que quiere, pero le da mucha libertad para construir su hiearchy:

sealed trait Fruit 

case class Orange extends Fruit with OrangeWriter 
case class Apple extends Fruit 
case class Banana extends Fruit 

trait Writer { 
    def write() 
} 

trait AppleWriter extends Writer { 
    self: Apple => 
    def write() {println("I am an apple!")} 
} 

trait OrangeWriter extends Writer { 
    self: Orange => 
    def write() {println("I am an orange!")} 
} 

def writeFruits(fruits:List[Fruit]) { 
    fruits.collect{case w:Writer => w}.foreach(_.write()) 
} 

writeFruits(List(Apple(), Orange(),Banana(), new Apple with AppleWriter)) 

Como se puede ver, se puede tener Fruit s que tienen siempre una Writer adjunta (aquí Orange s) y puede adjuntar Escritores "sobre la marcha" (el último Apple en el List).

Cuestiones relacionadas