Scala voor backend-engineering en functioneel programmeren · Les

Typeklassen en implicits

Leer typeklassen gebruiken voor ad-hocpolymorfisme en gebruik het implicit-systeem van Scala voor krachtige abstracties.

Les 3 van 311 stappen

Typeklassen en implicits is een gratis Scala voor backend-engineering en functioneel programmeren-les op CoddyKit. Dit is les 3 van 3. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Scala voor backend-engineering en functioneel programmeren. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Scala voor backend-engineering en functioneel programmeren bevat in totaal 3 lessen.

Ad-hocpolymorfisme uitgelegd

In Scala betekent polymorfisme dat je code schrijft die met verschillende typen werkt. Je hebt al subtypepolymorfisme met overerving gezien: een methode werkt dan voor een basisklasse en al haar subklassen.

Ad-hocpolymorfisme is anders. Hiermee kan één functie zich anders gedragen op basis van het specifieke type dat eraan wordt doorgegeven, zelfs als die typen geen relatie via overerving hebben. Hier komen typeklassen goed van pas!

Wat zijn typeklassen?

Een typeclass is een ontwerppatroon waarmee je nieuw gedrag aan bestaande typen kunt toevoegen zonder ze aan te passen en zonder overerving te gebruiken.

  • De typeclass definieert een contract (een trait) voor specifiek gedrag.
  • De typeclass biedt 'instanties' (objecten) die dit contract voor verschillende typen implementeren.
  • De typeclass gebruikt het implicit-mechanisme van Scala om automatisch het juiste gedrag binnen de scope beschikbaar te maken.

Een typeclass-trait definiëren

Eerst definiëren we een trait die het gewenste gedrag beschrijft. Deze trait heeft een typeparameter, A, die het type vertegenwoordigt waarop ons gedrag wordt toegepast.

We maken een Printable-typeclass die weet hoe je elk type omzet naar een voor mensen leesbare tekenreeks.

package com.coddykit

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

Instanties van typeclasses maken

Nu moeten we Scala vertellen hoe specifieke typen Printable worden. Dat doen we door voor elk type implicit objecten (of waarden) te maken die onze Printable-trait uitbreiden.

Deze worden typeclassinstanties genoemd.

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

Typeclasses gebruiken met implicits

Om onze typeclass te gebruiken, definiëren we een functie met een implicit parameter van het type van de typeclass. De compiler van Scala zoekt automatisch naar een beschikbare implicit-instantie van Printable[A] wanneer deze functie wordt aangeroepen.

Als de compiler er een vindt, injecteert deze de instantie in de functie!

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

Contextgrenzen: schonere syntaxis

Scala biedt een verkorte schrijfwijze voor implicit parameters, die een contextgrens wordt genoemd. In plaats van (implicit p: Printable[A]) kun je [A: Printable] schrijven.

Hierdoor wordt de code overzichtelijker, vooral wanneer je meerdere implicit parameters hebt.

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

Uitbreiden naar aangepaste typen

De kracht van typeclasses is dat je nieuw gedrag aan elk type kunt toevoegen, zelfs aan typen die niet van jou zijn (zoals Int of String), of aan je eigen aangepaste typen, zoals case classes.

We maken onze Person-case class 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: meer dan typeclasses

Hoewel typeclasses een belangrijk toepassingsgebied zijn, is het sleutelwoord implicit in Scala een breder mechanisme. Je kunt het gebruiken voor:

  • Implicit parameters: om automatisch waarden aan te leveren.
  • Implicit conversions: om het ene type automatisch naar een ander type om te zetten (gebruik dit voorzichtig, omdat de code daardoor moeilijker te volgen kan zijn).
  • Implicit values: om standaardwaarden voor bepaalde typen aan te leveren, bijvoorbeeld voor uitvoeringscontexten.

Het belangrijkste idee is dat de compiler binnen de scope zoekt naar geschikte implicit-definities.

Voordelen van typeclasses

Typeclasses bieden verschillende voordelen:

  • Uitbreidbaarheid: voeg nieuw gedrag toe aan bestaande typen zonder ze te wijzigen.
  • Ontkoppeling: scheid de typedefinitie van het gedrag ervan.
  • Ad-hocpolymorfisme: functies kunnen werken met elk type dat het vereiste gedrag biedt.
  • Testbaarheid: gedrag kan gemakkelijker afzonderlijk worden getest.
  • Geen overhead van overerving: je vermijdt de complexiteit van diepe overervingshiërarchieën.

Korte controle

Bekijk de Printable-typeclass en de instanties ervan uit deze les. Wat gebeurt er wanneer je print(10.5) aanroept, ervan uitgaande dat er geen DoublePrintable-instantie bestaat?

Samenvatting en volgende stappen

Je beheerst nu typeclasses en het krachtige implicit-systeem van Scala!

  • Typeclasses maken ad-hocpolymorfisme mogelijk, zodat je gedrag voor typen kunt definiëren zonder overerving.
  • Ze bestaan uit een trait (het contract) en implicit objecten/waarden (de instanties).
  • Implicit parameters en contextgrenzen worden door functies gebruikt om deze instanties automatisch te vinden en te gebruiken.

Typeclasses vormen een hoeksteen van functioneel programmeren in Scala. Ze worden veel gebruikt in bibliotheken zoals Cats en ZIO om flexibele en robuuste toepassingen te bouwen. Blijf oefenen om je begrip te verdiepen!

Gratis beginnen

Leer Scala met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
39
Lessen
143

Veelgestelde vragen

Is de les “Typeklassen en implicits” gratis?

Ja — de volledige tekst van “Typeklassen en implicits” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Scala voor backend-engineering en functioneel programmeren wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Scala voor backend-engineering en functioneel programmeren bevat in totaal 3 lessen.

Wat leer ik in “Typeklassen en implicits”?

Leer typeklassen gebruiken voor ad-hocpolymorfisme en gebruik het implicit-systeem van Scala voor krachtige abstracties. Je oefent met Scala voor backend-engineering en functioneel programmeren door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Scala voor backend-engineering en functioneel programmeren te beginnen?

Ervaring vooraf is niet nodig. Scala voor backend-engineering en functioneel programmeren op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 3.

Hoe lang duurt de les “Typeklassen en implicits”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Scala voor backend-engineering en functioneel programmeren?

Ja. Elke les over Scala voor backend-engineering en functioneel programmeren bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Generics en typeparameters
  2. Varianties: covariantie en contravariantie
  3. Typeklassen en implicits
← Terug naar Scala voor backend-engineering en functioneel programmeren