0Pricing
Scala for Backend Engineering & Functional Programming · Lezione

Il pattern dei type class

Polimorfismo ad hoc

Il pattern dei type class è una lezione Scala for Backend Engineering & Functional Programming gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Scala for Backend Engineering & Functional Programming, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Che cos'è una classe di tipi?

Una classe di tipi è un modello per aggiungere comportamento ai tipi senza modificarli. È una forma di polimorfismo ad hoc: la stessa operazione funziona con molti tipi non correlati, scelti dal compilatore in base al tipo.

Il concetto proviene da Haskell ed è ampiamente usato nelle librerie Scala come Cats.

Il problema che risolve

L'ereditarietà impone il comportamento a un tipo al momento della definizione. Tuttavia, spesso non può modificare un tipo, perché appartiene a una libreria, oppure desidera comportamenti diversi in contesti diversi. Le classi di tipi consentono di associare il comportamento esternamente.

Passaggio 1: definire il trait

Una classe di tipi è un trait parametrizzato da un tipo. Qui Show[A] descrive come eseguire il rendering di un qualunque A come String.

trait Show[A] {
  def show(value: A): String
}

@main def run(): Unit = {
  println("Show trait defined")
}

Passaggio 2: fornire le istanze

Per ogni tipo concreto che desidera supportare, crei un'instance del trait. Queste sono le istanze della classe di tipi.

trait Show[A] { def show(value: A): String }

object Main {
  val intShow: Show[Int] = (v: Int) => s"Int($v)"
  val strShow: Show[String] = (v: String) => s"Str($v)"

  def main(args: Array[String]): Unit = {
    println(intShow.show(7))
    println(strShow.show("hi"))
  }
}

Passaggio 3: usare l'istanza

Una funzione riceve l'istanza come parametro. La stessa funzione render funziona con qualunque tipo che disponga di un'istanza Show.

trait Show[A] { def show(value: A): String }

object Main {
  def render[A](value: A, s: Show[A]): String = s.show(value)

  val intShow: Show[Int] = (v: Int) => s"<$v>"

  def main(args: Array[String]): Unit = {
    println(render(99, intShow))
  }
}

Rendere l'istanza implicita

Passare le istanze manualmente è tedioso. Contrassegnando l'istanza e il parametro come implicit (o usando using in Scala 3), il compilatore può fornirli automaticamente.

trait Show[A] { def show(value: A): String }

object Main {
  implicit val intShow: Show[Int] = (v: Int) => s"<$v>"

  def render[A](value: A)(implicit s: Show[A]): String = s.show(value)

  def main(args: Array[String]): Unit = {
    println(render(42))
  }
}

Polimorfismo ad hoc

Lo stesso nome di funzione seleziona implementazioni diverse in base al tipo dell'argomento. Questo è il polimorfismo ad hoc, distinto dal polimorfismo per sottotipi (ereditarietà) e dal polimorfismo parametrico (generics).

trait Show[A] { def show(value: A): String }

object Main {
  implicit val intShow: Show[Int] = (v: Int) => s"int:$v"
  implicit val boolShow: Show[Boolean] = (v: Boolean) => s"bool:$v"

  def render[A](value: A)(implicit s: Show[A]): String = s.show(value)

  def main(args: Array[String]): Unit = {
    println(render(5))
    println(render(true))
  }
}

Lavorare con tipi personalizzati

Il vero vantaggio consiste nell'aggiungere comportamento ai propri tipi in modo pulito, mantenendo la classe dei dati libera da considerazioni di formattazione.

trait Show[A] { def show(value: A): String }

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

object Main {
  implicit val userShow: Show[User] = (u: User) => s"${u.name} (${u.age})"

  def render[A](value: A)(implicit s: Show[A]): String = s.show(value)

  def main(args: Array[String]): Unit = {
    println(render(User("Ada", 36)))
  }
}

Classi di tipi e interfacce

Un'interfaccia lega il comportamento alla definizione del tipo; una classe di tipi li disaccoppia.

  • Può aggiungere un'istanza di una classe di tipi a un tipo che non appartiene al proprio codice.
  • Può avere più istanze per contesti diversi.
  • Il comportamento viene selezionato dal compilatore nel punto della chiamata.

Tre componenti

Ogni classe di tipi ha tre componenti:

  • Il trait (l'operazione astratta).
  • Le istanze (le implementazioni per ciascun tipo).
  • L'interfaccia (le funzioni che richiedono un'istanza).

Esaminerà ciascun componente nelle lezioni successive.

Un esempio completo e minimale

Riunire il modello in un'unica funzione generica che usa un'istanza implicita.

trait Show[A] { def show(value: A): String }

object Main {
  implicit val intShow: Show[Int] = (v: Int) => s"#$v"
  implicit val strShow: Show[String] = (v: String) => '"' + v + '"'

  def printAll[A](xs: List[A])(implicit s: Show[A]): Unit =
    xs.foreach(x => println(s.show(x)))

  def main(args: Array[String]): Unit = {
    printAll(List(1, 2, 3))
    printAll(List("a", "b"))
  }
}

Verifica rapida

Verifichi la sua comprensione del modello delle classi di tipi.

Riepilogo

Ha imparato il modello delle classi di tipi:

  • Un trait parametrizzato descrive un'operazione.
  • Le istanze la implementano per ciascun tipo.
  • I parametri implicit consentono al compilatore di fornire l'istanza.
  • Questo offre polimorfismo ad hoc e consente di estendere tipi che non appartengono al proprio codice.

Domande Frequenti

La lezione «Il pattern dei type class» è gratuita?

Sì — il testo completo di «Il pattern dei type class» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Scala for Backend Engineering & Functional Programming, passa a CoddyKit PRO. Il corso Scala for Backend Engineering & Functional Programming include 4 lezioni in totale.

Cosa imparerò in «Il pattern dei type class»?

Polimorfismo ad hoc Eserciti Scala for Backend Engineering & Functional Programming con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Scala for Backend Engineering & Functional Programming?

Non è richiesta alcuna esperienza precedente. Scala for Backend Engineering & Functional Programming su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Il pattern dei type class»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Scala for Backend Engineering & Functional Programming?

Sì. Ogni lezione Scala for Backend Engineering & Functional Programming include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Il pattern dei type class
  2. Definire istanze
  3. Type class comuni
  4. Derivazione delle type class
← Torna a Scala for Backend Engineering & Functional Programming