Archivo de la etiqueta: type

Scala: selftypes

Tal y como comentamos en entradas anteriores, hoy toca hablar de selftypes (¿cómo?¿lo qué?¿mande?).

Se ilustra muy bien su utilidad con el siguiente ejemplo.
Suponed que estamos definiendo nuestro objeto singleton que representa un mostrador de facturación de un aeropuerto:

object Facturacion{
  type Identificador = String
  var idMaletas: List[Identificador] = List()
}

hasta aquí sin problemas, ¿no? Ahora pongamos que queremos añadir un método para procesar un registro de facturación: Un señor que quiere volar quiere facturar sus maletas.

object Facturacion{
  type Identificador = String
  var idMaletas: List[Identificador] = List()

  def facturar(nombre: String, idMaletas: List[Idenntificador) = {
    println(s"Registrando maletas del señor $nombre")
    idMaletas ++= idMaletas //WTF
  }
}

Exacto. Ocurre que tenemos el mismo nombre para designar tanto a la variable como al parámetro de la función.
La primera lógica es cambiarle el nombre a uno de los dos.

…pero ocurre que no queremos XD Porque nos guste el nombre tal y como está, porque no tenga sentido que sea otro, porque si digo 3 veces ‘selftype’ frente al espejo aparece Odersky y me saca los ojos…Elegid vuestro motivo favorito

¿La otra opción? Hacer referencia a la enclosing class (enclosing object en este caso) de la siguiente forma:

object Facturacion{
  esteMostrador =>
  //...
}

de manera que ahora podemos efectuar la asignación que antes no podíamos:

object Facturacion{
  esteMostrador =>

  type Identificador = String
  var idMaletas: List[Identificador] = List()

  def facturar(nombre: String, idMaletas: List[Idenntificador) = {
    println(s"Registrando maletas del señor $nombre")
    esteMostrador.idMaletas ++= idMaletas //YOLO!
  }
}

Los motivos que normalmente aconsejan el uso de una anotación selftype suelen ser:

  • Reforzar una cierta clase/trait base para asegurarse de que tu inner clase solo puede ser heredada o usada en mixin dentro de ese tipo.
  • Referenciar una clase exterior cuando se implementa una inner class.

Este último es el caso que nos atañe y, tenéis que reconocer, que como poco os acabo de arrancar una sonrisa con esta ventaja de Scala.

Hasta la próxima 🙂

Deja un comentario

Archivado bajo Tutoriales

Scala: tipos estructurales

Esta semana os traemos la magia de los tipos estructurales en Scala.
Directa a vuestras casas, motherfu subscribers.

Suponed que estáis usando en vuestro proyecto Scala, una librería de un tercero que tiene la siguiente clase:

class Printer {
  type DocumentId = Int
  val documentMaxNumber: Int = 3
  val address: String = "192.168.1.10"
  def queue(document: java.io.File): DocumentId = {
    //queue document and return its identifier
    //...
    Int.MaxValue
  }
  def dequeue(document: DocumentId): Unit = {
    //do some operations...
    //...and let's supose I've dequeued it...
  }
}

Una bonita clase que representa una impresora … o algo.
Pongamos, además, que tenéis en vuestro proyecto esta clase, que sí habéis definido vosotros y que simula el comportamiento de una impresora, sin llegar a implementar su lógica.

class MyPrinterMock {
  val address: String = "127.0.0.1"
  def queue(document: java.io.File): Printer#DocumentId = 1
  def dequeue(document: Printer#DocumentId)
}

Si ahora queremos definirnos en nuestra aplicación un método que reciba como argumento o una clase u otra para encolar un documento a la impresora, estaría muy bien definir un trait común al que extendieran ambas, pero eso no es posible (recordad que la clase Printer no es nuestra). De modo que tenemos varias opciones.

Opción A: El dinero

Podemos usar un Either:

object MyApp extends App{
  def queueAtPrinter(printer: Either[Printer,MyPrinterMock],documentPath: String) {
    printer match{
      case Left(realPrinter) =>
        println(realPrinter.address)
        realPrinter.queue(new java.io.File(documentPath))
      case Right(fakePrinter) =>
        println(fakePrinter.address)
        fakePrinter.queue(new java.io.File(documentPath))
    }
  }
}

Engorroso, ¿verdad? Repetimos código.

Opción B: La caja misteriosa

La otra opción es utilizar un tipo estructural.

type PrinterLike = {
  val address: String
  def queue(document: java.io.File): Printer#DocumentId
}

Este tipo simboliza «algo» que tiene un valor de tipo String denominado address y un método queue que recibe un File y devuelve un DocumentId.
De esta forma podemos definir el método ‘queueAtPrinter’ de la forma que sigue:

def queueAtPrinter(printer: PrinterLike,documentPath: String) {
  println(printer.address)
  printer.queue(new java.io.File(documentPath))
}

o incluso parametrizarlo sin utilizar el tipo PrinterLike:

def queueAtPrinter[P <: {val address: String
  def queue(document:java.io.File):Printer#DocumentId }](printer: P,documentPath: String) {

  //...
}

¿Qué escoger? Bajo mi punto de vista, lo más elegante es un tipo estructural.

Piénsalo, Lois: Un barco es un barco, pero la caja puede ser cualquier cosa, ¡incluso un barco!

Lo único que, como todas las cosas buenas, es importante no abusar. Los tipos estructurales hacen uso de las reflectiveCalls, es decir, reflexividad, y en función de cuanto lo uséis y la forma en que lo hagáis, esto puede impactar en el rendimiento de vuestra aplicación.

Tipos estructurales e Inyección de dependencias

Si recordáis en la anterior entrada sobre Cake Pattern, podíamos indicar que un trait o clase abstracta era dependiente de otros componentes.
Ahora que hemos visto tipos estructurales, imaginad cómo quedaría expresar que tu trait depende de algo que tiene un valor entero y otros componentes:

trait MiTraitDependiente {
  dependeDe: { val unEntero: Int} with OtroComponente =>
}
trait OtroComponente

c64715291456bedb270fffc3fd98c424

No os perdáis la próxima entrega para Scala: selftypes 🙂

Deja un comentario

Archivado bajo Tutoriales