Comprender lo que hace la palabra clave 'tipo' en Scala

144

Soy nuevo en Scala y realmente no pude encontrar mucho sobre la typepalabra clave. Estoy tratando de entender lo que puede significar la siguiente expresión:

type FunctorType = (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

FunctorType es una especie de alias, pero ¿qué significa?

Core_Dumped
fuente

Respuestas:

148

Sí, el alias de tipo FunctorType es solo una abreviatura de

(LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

Los alias de tipo a menudo se usan para mantener el resto del código simple: ahora puede escribir

def doSomeThing(f: FunctorType)

que será interpretado por el compilador como

def doSomeThing(f: (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate)

Esto ayuda a evitar la definición de muchos tipos personalizados que son solo tuplas o funciones definidas en otros tipos, por ejemplo.

También hay varios otros casos de uso interesantes para type, como se describe, por ejemplo, en este capítulo de Programación en Scala .

Roland Ewald
fuente
198

En realidad, la typepalabra clave en Scala puede hacer mucho más que solo asignar un tipo complicado a un nombre más corto. Introduce miembros tipo .

Como sabe, una clase puede tener miembros de campo y miembros de método. Bueno, Scala también permite que una clase tenga miembros de tipo.

En tu caso particular type , de hecho, está introduciendo un alias que le permite escribir código más conciso. El sistema de tipos simplemente reemplaza el alias con el tipo real cuando se realiza la verificación de tipos.

Pero también puedes tener algo como esto

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

Al igual que cualquier otro miembro de una clase, los miembros de tipo también pueden ser abstractos (simplemente no se especifica cuál es realmente su valor) y se pueden anular en las implementaciones.

Los miembros de tipo pueden verse como duales de genéricos, ya que muchas de las cosas que puede implementar con genéricos se pueden traducir en miembros de tipo abstracto.

Entonces, sí, se pueden usar para crear alias, pero no los limite a esto, ya que son una característica poderosa del sistema de tipos de Scala.

Vea esta excelente respuesta para más detalles:

Scala: tipos abstractos vs genéricos

Marius Danila
fuente
44
Es importante recordar que usar el tipo dentro de una clase crea un miembro de tipo en lugar de un alias. Entonces, si solo necesita un alias de tipo, defínalo en el objeto complementario.
Rüdiger Klaehn
9

Me gustó la respuesta de Roland Ewald, ya que describió con un caso de uso muy simple de alias de tipo, y para más detalles introdujo un tutorial muy agradable. Sin embargo, dado que se introduce otro caso de uso en esta publicación llamado miembros de tipo , me gustaría mencionar el caso de uso más práctico, que me gustó mucho: (esta parte está tomada de aquí :)

Tipo de resumen:

type T

T arriba dice que este tipo que se va a usar, aún se desconoce, y dependiendo de la subclase concreta, se definirá. La mejor manera de entender los conceptos de programación es proporcionar un ejemplo: suponga que tiene el siguiente escenario:

Sin tipo de abstracción

Aquí obtendrá un error de compilación, porque el método eat en las clases Cow y Tiger no anula el método eat en la clase Animal, porque sus tipos de parámetros son diferentes. Es hierba en la clase vaca y carne en la clase tigre vs. alimento en la clase animal, que es súper clase y todas las subclases deben cumplir.

Ahora, de vuelta a la abstracción de tipo, mediante el siguiente diagrama y simplemente agregando una abstracción de tipo, puede definir el tipo de entrada, de acuerdo con la propia subclase.

Con tipo abstracto

Ahora mira los siguientes códigos:

  val cow1: Cow = new Cow
  val cow2: Cow = new Cow

  cow1 eat new cow1.SuitableFood
  cow2 eat new cow1.SuitableFood

  val tiger: Tiger = new Tiger
  cow1 eat new tiger.SuitableFood // Compiler error

El compilador está contento y mejoramos nuestro diseño. Podemos alimentar a nuestra vaca con vaca. La comida y el compilador adecuados nos impiden alimentar a la vaca con la comida adecuada para el tigre. Pero, ¿qué pasa si queremos hacer una diferencia entre el tipo de vaca1 AdecuadoFood y vaca2 SuitabeFood? En otras palabras, sería muy útil en algunos escenarios si la ruta por la que llegamos al tipo (por supuesto, a través del objeto) es fundamental. Gracias a las funciones avanzadas de scala, es posible:

Tipos dependientes de la ruta: los objetos Scala pueden tener tipos como miembros. El significado del tipo depende de la ruta que use para acceder a él. La ruta está determinada por la referencia a un objeto (también conocido como una instancia de una clase). Para implementar este escenario, debe definir la clase Grass dentro de Cow, es decir, Cow es la clase externa y Grass es la clase interna. La estructura será así:

  class Cow extends Animal {
    class Grass extends Food
    type SuitableFood = Grass
    override def eat(food: this.SuitableFood): Unit = {}
  }

  class Tiger extends Animal {
    class Meat extends Food
    type SuitableFood = Meat
    override def eat(food: this.SuitableFood): Unit = {}
  }

Ahora, si intentas compilar este código:

  1. val cow1: Cow = new Cow
  2. val cow2: Cow = new Cow

  3. cow1 eat new cow1.SuitableFood
  4. cow2 eat new cow1.SuitableFood // compilation error

En la línea 4 verá un error porque Grass ahora es una clase interna de Cow, por lo tanto, para crear una instancia de Grass, necesitamos un objeto cow y este objeto cow determina el camino. Entonces 2 objetos de vaca dan lugar a 2 caminos diferentes. En este escenario, cow2 solo quiere comer alimentos especialmente creados para ello. Entonces:

cow2 eat new cow2.SuitableFood

Ahora todos están felices :-)

Mehran
fuente
5

Solo un ejemplo para ver cómo usar "type" como alias:

type Action = () => Unit

La definición anterior define Acción como un alias del tipo de procedimientos (métodos) que toman una lista de parámetros vacía y que devuelven Unidad.

sofiene zaghdoudi
fuente