Diferencia entre la herencia de rasgos y la anotación de tipo propio


En Scala, he visto las construcciones

trait T extends S

Y

trait T { this: S =>

Se usa para lograr cosas similares (es decir, que los métodos abstractos en S deben definirse antes de que se pueda crear una instancia). ¿Cuál es la diferencia entre ellos? ¿Por qué usarías uno sobre el otro?

Author: Ben Lings, 2010-02-09

6 answers

Usaría auto-tipos para la administración de dependencias: Este rasgo requiere que se mezcle otro rasgo. Y usaría herencia para refinar otro rasgo o interfaz.

Solo como ejemplo:

trait FooService

trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }

object Services extends FooService with FooRemoting with FooPersistence

Ahora, si FooRemoting y FooPersistence hubieran heredado de FooService, y FooService tiene miembros y métodos, ¿cómo se verían los Servicios?

Mientras que para la herencia, tendríamos algo como:

trait Iterator[T] {
  def hasNext : boolean
  def next : T
}

trait InfiniteIterator[T] extends Iterator[T] {
  def hasNext = true
}
 14
Author: Viktor Klang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-09 12:36:47

Las anotaciones de tipo propio le permiten expresar dependencias cíclicas. Por ejemplo:

trait A extends B
trait B { self: A => }

Esto no es posible con la herencia simple.

 26
Author: Joa Ebert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-08 21:48:09

Desde que hice la pregunta me encontré con estos mensajes:

Spiros Tzavellas habla de usar un trait como interfaz pública y el self type como ayudante que debe ser mezclado por la clase de implementación.

En conclusión, si queremos mover implementaciones de métodos dentro de traits entonces corremos el riesgo de contaminar la interfaz de esos rasgos con métodos abstractos que apoyan la aplicación de la métodos concretos y no están relacionados con el principal responsabilidad del rasgo. Una solución a este problema es para mover esos métodos abstractos en otros rasgos y componen los rasgos juntos usando anotaciones de tipo propio y herencia múltiple.

Por ejemplo:

trait PublicInterface { this: HelperTrait =>
  // Uses helperMethod
}

trait HelperTrait {
  def helperMethod = // ...
}

class ImplementationClass extends PublicInterface with HelperTrait

Un recorrido por Scala discute el uso de anotaciones de tipo propio con miembros de tipo abstracto-presumiblemente no es posible extend un miembro de tipo abstracto(?)

 8
Author: Ben Lings,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-08 22:57:35

La respuesta es "circularidad". Pero no solo.

La anotación de tipo propio resuelve para mí el problema fundamental de la herencia: lo que heredas no puede usar lo que eres. Con el tipo de uno mismo, todo se vuelve fácil.

Mi patrón es el siguiente y puede ser considerado como un pastel degenerado:

trait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }

Puede explotar su clase en múltiples comportamientos que se pueden llamar desde cualquier lugar del ensamblado, mientras permanece escrito limpiamente. No hay necesidad de la indirecta dolorosa demasiado a menudo (y erróneamente) identificado con el patrón de pastel.

La mitad (si no la totalidad) de los enrevesados frameworks Java DI de los últimos diez años se han dedicado a hacer esto, por supuesto sin la tipificación. Las personas que todavía usan JAVA en este dominio están perdiendo claramente su tiempo: "SCALA ouakbar".

 2
Author: wiki1000,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-23 22:38:10

Sé que esta pregunta es antigua, pero me gustaría añadir algunas aclaraciones y ejemplos.

Hay tres diferencias principales entre la herencia de rasgos y los tipos de uno mismo.

Semántica

La herencia es una de las relaciones con el mayor acoplamiento del paradigma del objeto, si A extiende B, eso significa que A es B.{[17]]}

Digamos que tenemos el siguiente código,

trait Animal {
  def stop():Unit = println("stop moving")
}

class Dog extends Animal {
  def bark:String = "Woof!"
}

val goodboy:Dog = new Dog

goodboy.bark
// Woof!

, Estamos diciendo que un Perro es un Animal. Podemos enviar los mensajes bark y stop a goodboy porque es un perro, entiende ambos métodos.

Ahora supongamos que tenemos un nuevo rasgo,{[17]]}

trait Security {
  this: Animal =>
  def lookout:Unit = { stop(); println("looking out!") }
}

Esta vez la Seguridad NO es un Animal, y eso está bien porque sería semánticamente incorrecto si afirmamos que una Seguridad es un Animal, son conceptos diferentes, que se pueden usar juntos.

Así que ahora podemos crear un nuevo tipo de perro,

val guardDog = new Dog with Security

guardDog.lookout
// stop moving
// looking out!

guardDog es un Perro, un Animal y la Seguridad. Se entiende stop, bark y lookout porque es un Perro con Seguridad.

Pero, ¿qué pasa si creamos un perro nuevo como este?

val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!

guardDog2 es solo un perro, por lo que no podemos llamar lookout método. (okok, es un Perro con Seguridad, pero solo vemos un Perro)

Dependencias cíclicas

Los Self Types nos permiten crear dependencias cíclicas entre tipos.

trait Patient {
  this: Reader =>
  def isQuite:Boolean = isReading
  def isSlow:Boolean = true
}

trait Reader {
  this: Patient =>
  def read():Unit = if(isSlow) println("Reading Slow...") else println("Reading Fast...")
  def isReading = true
}

val person = new Patient with Reader

El siguiente código no compila.

trait Patient extends Reader { /** code **/}

trait Reader extends Patient { /** code **/ }

Este tipo de código es muy común en la inyección de dependencias (cake patrón).

Versatilidad

Por último, pero no menos importante, quien utiliza nuestros rasgos puede decidir el orden en el que se utilizan, por lo que gracias a la Linealización de Rasgos el resultado final puede ser diferente aunque los rasgos utilizados sean los mismos.

Con la herencia normal no podemos hacer eso, las relaciones entre rasgos y clases son fijas.

trait Human {
  def isGoodForSports:Boolean
}

trait Programmer extends Human {
  def readStackOverflow():Unit = println("Reading...")
  override def isGoodForSports: Boolean = false
}

trait Sportsman extends Human {
  def play():Unit = println("Playing something")
  override def isGoodForSports: Boolean = true
}

val foo = new Programmer with Sportsman
foo.isGoodForSports
// true

val bar = new Sportsman with Programmer
bar.isGoodForSports
// false

Espero que esto pueda ser útil.

 2
Author: Sebastian Celestino,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-08 02:58:23

Aunque no responde a su pregunta, estaba tratando de entender las anotaciones de tipo propio y básicamente me perdí en las respuestas, y de alguna manera terminé pasando por variaciones de su pregunta, que se centra en el uso de anotaciones de tipo propio para indicar dependencias.

Así que aquí posteo una descripción de un caso de uso donde las anotaciones de tipo propio están bien ilustradas, es decir, algo así como un caso seguro de tipo de 'esto' como un subtipo:

Http://programming-scala.labs.oreilly.com/ch13.html#SelfTypeAnnotationsAndAbstractTypeMembers

Con la esperanza de que sería útil para aquellos que terminan en esta pregunta por casualidad (y, como yo, no tuvo tiempo de leer un libro de scala antes de comenzar a explorar :-) )

 1
Author: ithkuil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-11 15:49:03