Typklasser och implicita värden
Lär er använda typklasser för ad hoc-polymorfism och utnyttja Scalas system med implicita värden för kraftfulla abstraktioner.
Typklasser och implicita värden är en gratis lektion i Scala för backendutveckling och funktionell programmering på CoddyKit. Detta är lektion 3 av 3. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för Scala för backendutveckling och funktionell programmering, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Scala för backendutveckling och funktionell programmering innehåller totalt 3 lektioner.
Ad hoc-polymorfism förklarad
I Scala innebär polymorfism att skriva kod som fungerar med olika typer. Ni har sett subtypningspolymorfism med arv, där en metod fungerar för en basklass och alla dess subklasser.
Ad hoc-polymorfism fungerar annorlunda. Den gör att en enda funktion kan bete sig olika beroende på den specifika typ den får, även om typerna inte är relaterade genom arv. Det är här Type Classes kommer till sin rätt!
Vad är typklasser?
En Type Class är ett designmönster som gör det möjligt att lägga till nytt beteende i befintliga typer utan att ändra dem och utan att använda arv.
- Den definierar ett kontrakt (ett trait) för ett specifikt beteende.
- Den tillhandahåller ”instanser” (objekt) som implementerar detta kontrakt för olika typer.
- Den använder Scalas implicit-mekanism för att automatiskt göra rätt beteende tillgängligt i det aktuella scopet.
Definiera ett Type Class-trait
Först definierar vi ett trait som beskriver det beteende vi vill ha. Detta trait tar en typparameter, A, som representerar den typ som beteendet ska tillämpas på.
Vi skapar en Printable-type class som vet hur man konverterar valfri typ till en läsbar sträng.
package com.coddykit
trait Printable[A] {
def format(value: A): String
}Skapa Type Class-instanser
Nu måste vi tala om för Scala hur specifika typer ska göras Printable. Det gör vi genom att skapa implicit-objekt (eller värden) som utökar vårt Printable-trait för varje typ.
Dessa kallas type class-instanser.
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"
}
}Använda Type Classes med Implicits
För att använda vår type class definierar vi en funktion som tar en implicit parameter av type class-typen. Scalas kompilator söker automatiskt efter en tillgänglig implicit-instans av Printable[A] när funktionen anropas.
Om den hittar en sådan injicerar den den i funktionen!
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)
}
}Context Bounds: renare syntax
Scala erbjuder en kortare syntax, kallad context bound, för implicita parametrar. I stället för (implicit p: Printable[A]) kan du skriva [A: Printable].
Det gör koden renare, särskilt när du har flera implicita parametrar.
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)
}
}Utöka med anpassade typer
Styrkan hos type classes är att du kan lägga till nya beteenden i vilken typ som helst, även typer som du inte äger (som Int eller String), eller dina egna anpassade typer, till exempel case classes.
Vi gör vår 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: mer än Type Classes
Även om type classes är ett viktigt användningsområde är nyckelordet implicit i Scala en mer generell mekanism. Det kan användas för:
- Implicit Parameters: Som med type classes, för att automatiskt tillhandahålla värden.
- Implicit Conversions: För att automatiskt konvertera en typ till en annan (använd med försiktighet, eftersom de kan göra koden svårare att följa).
- Implicit Values: För att tillhandahålla standardvärden för vissa typer, ofta exempelvis för execution contexts.
Grundidén är att kompilatorn söker efter lämpliga implicita definitioner i det aktuella scopet.
Fördelar med Type Classes
Type classes har flera fördelar:
- Extensibility: Lägg till nya beteenden i befintliga typer utan att ändra dem.
- Decoupling: Separera typdefinitionen från dess beteende.
- Ad-hoc Polymorphism: Funktioner kan arbeta med alla typer som tillhandahåller det efterfrågade beteendet.
- Testability: Det blir enklare att testa beteenden isolerat.
- No Inheritance Overhead: Undvik komplexiteten hos djupa arvshierarkier.
Snabb kontroll
Tänk på Printable-type classen och dess instanser från lektionen. Om du anropar print(10.5), och vi antar att ingen DoublePrintable-instans finns, vad händer då?
Sammanfattning och nästa steg
Du har nu lärt dig Type Classes och Scalas kraftfulla implicit-system!
- Type Classes möjliggör ad hoc-polymorfism och låter dig definiera beteenden för typer utan arv.
- De består av ett trait (kontraktet) och implicit-objekt/värden (instanserna).
- Implicit Parameters och context bounds används av funktioner för att automatiskt hitta och använda dessa instanser.
Type classes är en grundpelare inom funktionell programmering i Scala och används flitigt i bibliotek som Cats och ZIO för att bygga flexibla och robusta applikationer. Fortsätt öva för att befästa dina kunskaper!
Lär dig Scala med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 39
- Lektioner
- 143
Vanliga frågor
Är lektionen ”Typklasser och implicita värden” gratis?
Ja – hela texten till ”Typklasser och implicita värden” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i Scala för backendutveckling och funktionell programmering, kan Ni uppgradera till CoddyKit PRO. Kursen i Scala för backendutveckling och funktionell programmering innehåller totalt 3 lektioner.
Vad lär jag mig i ”Typklasser och implicita värden”?
Lär er använda typklasser för ad hoc-polymorfism och utnyttja Scalas system med implicita värden för kraftfulla abstraktioner. Ni övar på Scala för backendutveckling och funktionell programmering med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Scala för backendutveckling och funktionell programmering?
Du behöver inga förkunskaper. Utbildningen i Scala för backendutveckling och funktionell programmering på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 3.
Hur lång tid tar lektionen ”Typklasser och implicita värden”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Scala för backendutveckling och funktionell programmering-lektionen?
Ja. Varje Scala för backendutveckling och funktionell programmering-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Generiska typer och typparametrar
- Varians: kovarians och kontravarians
- Typklasser och implicita värden