0Pricing
Scala for Backend Engineering & Functional Programming · Leçon

Classes de types et implicites

Apprenez à utiliser les classes de types pour le polymorphisme ad hoc et à tirer parti du système d’implicites de Scala pour créer des abstractions puissantes.

Classes de types et implicites est une leçon Scala for Backend Engineering & Functional Programming gratuite sur CoddyKit. Ceci est la leçon 3 sur 3. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Scala for Backend Engineering & Functional Programming, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Scala for Backend Engineering & Functional Programming comprend 3 leçons au total.

Le polymorphisme ad hoc expliqué

En Scala, le polymorphisme consiste à écrire du code qui fonctionne avec différents types. Vous avez déjà vu le polymorphisme par sous-typage avec l'héritage, lorsqu'une méthode fonctionne pour une classe de base et pour toutes ses sous-classes.

Le polymorphisme ad hoc est différent. Il permet à une même fonction d'avoir un comportement différent selon le type précis qui lui est fourni, même si ces types ne sont pas liés par héritage. C'est là que les classes de types révèlent toute leur utilité !

Que sont les classes de types ?

Une classe de types est un patron de conception qui permet d’ajouter un nouveau comportement à des types existants sans les modifier et sans utiliser l’héritage.

  • Elle définit un contrat (un trait) pour un comportement spécifique.
  • Elle fournit des « instances » (des objets) qui implémentent ce contrat pour différents types.
  • Elle utilise le mécanisme implicite de Scala pour placer automatiquement le comportement approprié dans la portée.

Définir un trait de classe de types

Commençons par définir un trait qui décrit le comportement souhaité. Ce trait prend un paramètre de type, A, qui représente le type sur lequel notre comportement va agir.

Créons une classe de types Printable capable de convertir n’importe quel type en une chaîne lisible par un être humain.

package com.coddykit

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

Créer des instances de classe de types

Nous devons maintenant indiquer à Scala comment rendre certains types Printable. Pour cela, nous créons des objets implicites (ou des valeurs) qui étendent notre trait Printable pour chaque type.

On les appelle des instances de classe de types.

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"
  }
}

Utiliser les classes de types avec des implicites

Pour utiliser notre classe de types, nous définissons une fonction qui prend un paramètre implicite de cette classe de types. Le compilateur de Scala recherche automatiquement une instance implicite disponible de Printable[A] lors de l’appel de cette fonction.

S’il en trouve une, il l’injecte dans la fonction !

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)
  }
}

Bornes de contexte : une syntaxe plus claire

Scala propose une notation abrégée appelée borne de contexte pour les paramètres implicites. Au lieu d’écrire (implicit p: Printable[A]), vous pouvez écrire [A: Printable].

Le code est ainsi plus clair, en particulier lorsque plusieurs paramètres implicites sont utilisés.

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)
  }
}

Étendre les classes de types personnalisées

La puissance des classes de types vient du fait que vous pouvez ajouter de nouveaux comportements à n’importe quel type, y compris à des types que vous ne possédez pas (comme Int ou String), ou à vos propres types personnalisés tels que les classes de cas.

Rendons notre classe de cas Person 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)
  }
}

Les implicites : plus que les classes de types

Bien que les classes de types constituent un cas d’utilisation majeur, le mot-clé implicit de Scala est un mécanisme plus général. Il peut servir à :

  • Paramètres implicites : fournir automatiquement des valeurs, comme avec les classes de types.
  • Conversions implicites : convertir automatiquement un type en un autre (à utiliser avec prudence, car elles peuvent rendre le code plus difficile à suivre).
  • Valeurs implicites : fournir des valeurs par défaut pour certains types, souvent pour des éléments tels que les contextes d’exécution.

L’idée essentielle est que le compilateur recherche les définitions implicites appropriées dans la portée.

Avantages des classes de types

Les classes de types offrent plusieurs avantages :

  • Extensibilité : ajouter un nouveau comportement à des types existants sans les modifier.
  • Découplage : séparer la définition d’un type de son comportement.
  • Polymorphisme ad hoc : les fonctions peuvent fonctionner avec tout type fournissant le comportement requis.
  • Testabilité : tester plus facilement les comportements de manière isolée.
  • Absence de surcharge liée à l’héritage : éviter la complexité des hiérarchies d’héritage profondes.

Vérification rapide

Considérez la classe de types Printable et ses instances présentées dans cette leçon. Si vous appelez print(10.5), en supposant qu’aucune instance DoublePrintable n’existe, que se passera-t-il ?

Récapitulatif et prochaines étapes

Vous maîtrisez désormais les classes de types et le puissant système d’implicites de Scala !

  • Les classes de types permettent le polymorphisme ad hoc, en vous laissant définir un comportement pour des types sans recourir à l’héritage.
  • Elles se composent d’un trait (le contrat) et d’objets/valeurs implicites (les instances).
  • Les paramètres implicites et les bornes de contexte permettent aux fonctions de trouver et d’utiliser automatiquement ces instances.

Les classes de types sont un pilier de la programmation fonctionnelle en Scala. Elles sont largement utilisées dans des bibliothèques comme Cats et ZIO pour créer des applications flexibles et robustes. Continuez à vous exercer afin de consolider votre compréhension !

Questions Fréquemment Posées

La leçon « Classes de types et implicites » est-elle gratuite ?

Oui — le texte complet de « Classes de types et implicites » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Scala for Backend Engineering & Functional Programming, passe à CoddyKit PRO. Le cours Scala for Backend Engineering & Functional Programming comprend 3 leçons au total.

Qu'est-ce que j'apprendrai dans « Classes de types et implicites » ?

Apprenez à utiliser les classes de types pour le polymorphisme ad hoc et à tirer parti du système d’implicites de Scala pour créer des abstractions puissantes. Tu pratiques Scala for Backend Engineering & Functional Programming avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Scala for Backend Engineering & Functional Programming ?

Aucune expérience préalable n'est requise. Scala for Backend Engineering & Functional Programming sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 3.

Combien de temps prend la leçon « Classes de types et implicites » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Scala for Backend Engineering & Functional Programming ?

Oui. Chaque leçon Scala for Backend Engineering & Functional Programming inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Génériques et paramètres de type
  2. Variance : covariance et contravariance
  3. Classes de types et implicites
← Retour à Scala for Backend Engineering & Functional Programming