0Pricing
Scala for Backend Engineering & Functional Programming · Lección

Clases de tipos e implicits

Aprenda a utilizar clases de tipos para el polimorfismo ad hoc y a aprovechar el sistema de implícitos de Scala para crear abstracciones potentes.

Clases de tipos e implicits es una lección gratuita de Scala for Backend Engineering & Functional Programming en CoddyKit. Esta es la lección 3 de 3. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Scala for Backend Engineering & Functional Programming, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Scala for Backend Engineering & Functional Programming incluye 3 lecciones en total.

Polimorfismo ad hoc explicado

En Scala, polimorfismo significa escribir código que funcione con distintos tipos. Ha visto el polimorfismo por subtipado mediante la herencia, donde un método funciona para una clase base y todas sus subclases.

El polimorfismo ad hoc es diferente. Permite que una misma función se comporte de forma distinta según el tipo concreto que recibe, aunque esos tipos no estén relacionados mediante herencia. ¡Aquí es donde destacan las clases de tipos!

¿Qué son las clases de tipos?

Una clase de tipos es un patrón de diseño que permite añadir un comportamiento nuevo a tipos existentes sin modificarlos y sin utilizar herencia.

  • Define un contrato (un trait) para un comportamiento específico.
  • Proporciona «instancias» (objetos) que implementan este contrato para distintos tipos.
  • Utiliza el mecanismo de implicit de Scala para incorporar automáticamente al ámbito el comportamiento adecuado.

Definición de un trait de clase de tipos

Primero, definimos un trait que describe el comportamiento que queremos. Este trait acepta un parámetro de tipo, A, que representa el tipo sobre el que actuará nuestro comportamiento.

Creemos una clase de tipos Printable que sepa convertir cualquier tipo en una cadena legible para las personas.

package com.coddykit

trait Printable[A] {
  def format(value: A): String
}

Creación de instancias de clases de tipos

Ahora debemos indicar a Scala cómo hacer que determinados tipos sean Printable. Para ello, creamos objetos implicit (o valores) que extienden nuestro trait Printable para cada tipo.

Estas se denominan instancias de clases de tipos.

package com.coddykit

trait Printable[A] {
  def format(value: A): String
}

object PrintableInstances {
  implicit object StringPrintable extends Printable[String] {
    def format(value: String): String = s"'${value}'"
  }

  implicit object IntPrintable extends Printable[Int] {
    def format(value: Int): String = s"${value}i"
  }
}

Uso de clases de tipos con implicits

Para utilizar nuestra clase de tipos, definimos una función que acepta un parámetro implicit de la clase de tipos. El compilador de Scala buscará automáticamente una instancia implicit disponible de Printable[A] cuando se llame a esta función.

Si encuentra una, ¡la inyectará en la función!

package com.coddykit

trait Printable[A] {
  def format(value: A): String
}

object PrintableInstances {
  implicit object StringPrintable extends Printable[String] {
    def format(value: String): String = s"'${value}'"
  }

  implicit object IntPrintable extends Printable[Int] {
    def format(value: Int): String = s"${value}i"
  }
}

object PrintableSyntax {
  def print[A](value: A)(implicit p: Printable[A]): Unit = {
    println(p.format(value))
  }
}

object Main {
  import PrintableInstances._
  import PrintableSyntax._

  def main(args: Array[String]): Unit = {
    print("hello")
    print(123)
  }
}

Límites de contexto: sintaxis más limpia

Scala ofrece una abreviatura llamada límite de contexto para los parámetros implicit. En lugar de (implicit p: Printable[A]), puede escribir [A: Printable].

Esto hace que el código sea más limpio, especialmente cuando tiene varios parámetros implicit.

package com.coddykit

trait Printable[A] {
  def format(value: A): String
}

object PrintableInstances {
  implicit object StringPrintable extends Printable[String] {
    def format(value: String): String = s"'${value}'"
  }

  implicit object IntPrintable extends Printable[Int] {
    def format(value: Int): String = s"${value}i"
  }
}

object PrintableSyntax {
  // Using context bound: [A: Printable] is shorthand for (implicit p: Printable[A])
  def print[A: Printable](value: A): Unit = {
    // To access the implicit instance, use implicitly[Printable[A]]
    val p = implicitly[Printable[A]]
    println(p.format(value))
  }
}

object Main {
  import PrintableInstances._
  import PrintableSyntax._

  def main(args: Array[String]): Unit = {
    print("world")
    print(456)
  }
}

Extensión a tipos personalizados

La potencia de las clases de tipos reside en que permiten añadir comportamientos nuevos a cualquier tipo, incluso a tipos que no le pertenecen (como Int o String), o a sus propios tipos personalizados, como las clases case.

Hagamos que nuestra clase case Person sea Printable.

package com.coddykit

trait Printable[A] {
  def format(value: A): String
}

case class Person(name: String, age: Int)

object PrintableInstances {
  implicit object StringPrintable extends Printable[String] {
    def format(value: String): String = s"'${value}'"
  }

  implicit object IntPrintable extends Printable[Int] {
    def format(value: Int): String = s"${value}i"
  }

  // New instance for our custom Person type
  implicit object PersonPrintable extends Printable[Person] {
    def format(person: Person): String = 
      s"Person(name: ${person.name}, age: ${person.age})"
  }
}

object PrintableSyntax {
  def print[A: Printable](value: A): Unit = {
    val p = implicitly[Printable[A]]
    println(p.format(value))
  }
}

object Main {
  import PrintableInstances._
  import PrintableSyntax._

  def main(args: Array[String]): Unit = {
    val alice = Person("Alice", 30)
    print(alice)
  }
}

Implicits: mucho más que clases de tipos

Aunque las clases de tipos son un caso de uso principal, la palabra clave implicit de Scala es un mecanismo más amplio. Puede utilizarse para:

  • Parámetros implicit: como hemos visto con las clases de tipos, para proporcionar valores automáticamente.
  • Conversiones implícitas: para convertir automáticamente un tipo en otro (utilícelas con precaución, ya que pueden dificultar el seguimiento del código).
  • Valores implicit: para proporcionar valores predeterminados para determinados tipos, algo que se utiliza a menudo con contextos de ejecución.

La idea clave es que el compilador busca definiciones implicit adecuadas en el ámbito.

Ventajas de las clases de tipos

Las clases de tipos ofrecen varias ventajas:

  • Extensibilidad: permite añadir comportamientos nuevos a tipos existentes sin modificarlos.
  • Desacoplamiento: separa la definición del tipo de su comportamiento.
  • Polimorfismo ad hoc: las funciones pueden trabajar con cualquier tipo que proporcione el comportamiento requerido.
  • Facilidad de prueba: facilita probar los comportamientos de forma aislada.
  • Sin sobrecarga de herencia: evita las complejidades de las jerarquías de herencia profundas.

Comprobación rápida

Considere la clase de tipos Printable y sus instancias de nuestra lección. Si llama a print(10.5), suponiendo que no existe ninguna instancia de DoublePrintable, ¿qué ocurriría?

Repaso y próximos pasos

¡Ha dominado las clases de tipos y el potente sistema de implícitos de Scala!

  • Las clases de tipos permiten el polimorfismo ad hoc, lo que le permite definir comportamientos para tipos sin usar herencia.
  • Constan de un trait (el contrato) y objetos/valores implícitos (las instancias).
  • Las parámetros implícitos y los límites de contexto permiten que las funciones encuentren y utilicen automáticamente estas instancias.

Las clases de tipos son fundamentales en la programación funcional con Scala. Se utilizan ampliamente en bibliotecas como Cats y ZIO para crear aplicaciones flexibles y robustas. ¡Siga practicando para afianzar sus conocimientos!

Preguntas frecuentes

¿La lección «Clases de tipos e implicits» es gratis?

Sí — el texto completo de «Clases de tipos e implicits» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Scala for Backend Engineering & Functional Programming, actualiza a CoddyKit PRO. El curso de Scala for Backend Engineering & Functional Programming incluye 3 lecciones en total.

¿Qué aprenderé en «Clases de tipos e implicits»?

Aprenda a utilizar clases de tipos para el polimorfismo ad hoc y a aprovechar el sistema de implícitos de Scala para crear abstracciones potentes. Practicas Scala for Backend Engineering & Functional Programming con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Scala for Backend Engineering & Functional Programming?

No se requiere experiencia previa. Scala for Backend Engineering & Functional Programming en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 3.

¿Cuánto tiempo toma la lección «Clases de tipos e implicits»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Scala for Backend Engineering & Functional Programming?

Sí. Cada lección de Scala for Backend Engineering & Functional Programming incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Genéricos y parámetros de tipo
  2. Varianza: covarianza y contravarianza
  3. Clases de tipos e implicits
← Volver a Scala for Backend Engineering & Functional Programming